用这个新的、生动的图形模拟器拓展您的 Java 编程技能

CodeRuler 源自于 2004 ACM International Collegiate Programming Competition (请参阅 参考资料),它是 IBM alphaWorks 最新的幻想游戏模拟器挑战。这个游戏有一个简单的设定:您是自己的中世纪王国的统治者。您的农民和骑士有赖于您明智的战略思想、灵活的应变能力和过人的 Java 编程技能以生存、发展和繁荣。作为游戏参与者,您的目标是编写模拟这个统治者的 Java 代码。游戏模拟器让您的统治者对付六个对立的统治者(或者内置的示例统治者)并决定赢家。

本文引导您以简捷的方式统治您的中世纪王国。它揭示了游戏的环境、描述了规则、讨论了总策略,并提供了两个可以立即使用(或者修改)的、完善的可用统治者。

模拟环境
CodeRuler 是一个图形、生动的模拟游戏环境。作为一个中世纪统治者,您必须与其他统治者争夺土地和势力。您的王国包括:

  • 农民,可以占据和耕种土地。
  • 骑士,可以作战和俘获其他统治者的农民、骑士或占领城堡。
  • 一座可以产生更多骑士和农民的城堡。拥有的土地越多,产生他们的速度越快。

图形游戏世界
游戏在一个王国地图所描绘的二维世界中展开的。(背景地形草图只是作为墙纸,它不影响游戏的玩法或者改变游戏的进展。)图 1 展示了正在进行中的 CodeRuler 游戏。

图 1. 运行中的 CodeRuler

图 1 显示两个对抗的统治者。统治者——游戏对象战略性活动背后的策划者——不出现在游戏世界中。游戏对象(农民、骑士和城堡)是在模拟世界中移动的彩色点阵。图 2 展示了对象的形状及它们可能的移动方向。

图 2. CodeRuler 游戏对象的移动模式

从图 2 中可以看出,骑士和农民使用同样的移动模式。每回合,他们都沿着 8 种可能方向中的一个方向移动一格。每个方向有一个相关联的数字,您在 Java 编码中将使用这个数字。每一个数字还有一个预定义的常量(如 NW),在代码中会使用这些常量。

控制台得分显示
可以在 图 1 的右侧看到状态控制台。 当前游戏中的统治者和他们所属的团体的名称出现在控制台的顶部。这两个数字是统治者的当前得分(左边)和农民拥有的土地方格数。图 3 展示了一个示例得分显示。

图 3. 控制台得分显示

在图 3 中,#18 号统治者名为 Simple Ruler from IBM developerWorks。这个统治者的当前得分是 123,这个统治者的王国占有 774 个方格的土地。可以在任何时候单击右上方的红色 X 中止模拟。

土地占有显示一览
可以在 图 1 的状态控制台的中间看到缩小的世界。从图 4 中,很容易看出蓝色统治者占用的土地明显比洋红色的统治者占有的多。

图 4. 土地占有一览

模拟时钟
在 图 1 状态控制台的底部是一个时钟。图 5 显示了特写图像。

图 5. CodeRuler 时钟

一个太阳沿着时钟的钟面移动行。当太阳移动了完整的一周时游戏就结束了。时钟的每一次跳动是模拟器一个回合。作为统治者,您决定每一回合自己的对象所作的移动。

战斗规则
每一个统治者最初控制:

  • 10 个农民
  • 10 个骑士
  • 1 个城堡

创造新农民和城堡
城堡创造农民或者骑士的速度取决于所拥有的土地方格数:

拥有的土地 创造一个农民或者骑士所需要的回合数
124 或者更少 不创造
125 14
250 12
500 10
1,000 8
2,000 6
多于 4,000 4

在游戏过程中,要:

  • 利用农民占据尽可能多的土地(并保持占据状态)。
  • 利用骑士俘获对手尽可能多的农民,使他们不再占据土地。
  • 利用骑士与对手的骑士战斗并俘获尽可能多的骑士。这会削弱对手的防卸能力。
  • 利用骑士攻击夺取其他统治者的城堡。城堡是生产骑士和农民的工厂,没有它们就不能创造更多的农民或者骑士。如果有多个城堡,创造农民或骑士的速度就可以比对手更快。有关创造速度请参阅侧栏 创造新农民和城堡。
  • 战略上防止自己的城堡失陷。

俘获游戏对象
只有骑士可以俘获对手的农民、城堡或者骑士。只要将它移动移动到农民和城堡的方格中,就可以俘获他们。要俘获对手的骑士,必须首先将它的体力值降低到 0。每一个骑士一开始的体力都是 100,对手每次攻击俘获,它都会随机损失 15 到 30 之间的一个体力值。执行成功俘获的骑士得到 20 个体力单位。

得分方案
要赢得游戏,在游戏结束时您所扮演的统治者必须有最高的得分。注意赢家拥有的土地可能是最多的,也可能不是。表 1 给出了游戏的得分方案。

表 1. 俘获的得分方案

如果... 那么得到
俘获一个农民 4 分
俘获一个骑士 6 分
占领一座城堡 15 分

在游戏结束时,所剩下的对象、占领的城堡和占据的土地都添加到分数中,如表 2 所示。

表 2. 剩余对象的得分方案

剩余的对象 得分
农民 1 分
骑士 2 分
城堡 25 分
土地 每 10 个方格 1 分

游戏细节
每个玩家都编写模拟统治者的 Java 代码。游戏模拟器使您的统治者与其他统治者比赛并决定胜利者。在代码中,必须协调农民、骑士和城堡的移动。一组 API 提供了关于您的对象和其他竞争统治者的对象的信息。使用这组 API,可以编写实现进攻、防御、甚至随机应变的战略的代码。

见识 CodeRuler 背后的策划者
要了解 CodeRuler 引擎的内幕及高级战略的一些思路,请参阅对 Code Ruler 的创造者 Tim deBoer 的揭开内幕的 采访。

游戏的组件
CodeRuler 游戏要求用 Eclipse IDE 编写、调试和测试自己的统治者代码。(请参阅本文后面的 Eclipse:集成的王国开发环境 。)

CodeRuler 包括:

  • 一个交互式的游戏模拟器,作为 Eclipse IDE 的插件。
  • 可以用来编写统治者的 API 的文档。
  • 一组移动、俘获和得分规则。
  • 一个让自已的统治者与一组示例统治者对抗的本地竞技场。
  • 一个联网机制,用于提交统治者,以参与公开赛或者设立自己的锦标赛。

模拟世界坐标系统
这个游戏是在一个包含 4608 个方格--宽 72 个方格、高 64 个方格——的模拟世界中展开的。方格是按 (xy) 坐标系统编号的。x 轴从左到右,y 轴从上到下。图 6 显示了 CodeRuler 世界的布局。位置 (0,0) 在左上角。

图 6. CodeRuler 世界坐标系统

CodeRuler API 和继承层次
在命令游戏对象之前,需要理解 CodeRuler API。这个 API 是高度面向对象的,并有明确的继承层次。理解层次结构是编写高效 CodeRuler 代码的关键。图 7 显示了继承层次结构。

图 7. CodeRuler 继承层次结构

图 7 中的继承树基于 Java 接口。每一个游戏对象都必须实现它相关的接口:农民必须实现 IPeasant、骑士必须实现 IKnight 等。不过,永远不需要编写其中任何类,因为 CodeRuler 模拟器使用内置的实现。作为一个统治者,只有在需要得到关于游戏对象的信息时才需要使用接口提供的 API。

IObject 接口
IObject 接口是游戏中所有对象的父接口。每一个对象间接实现 IObjectIObject 提取了所有对象的共有行为:

  • getRuler():对象所属的统治者
  • getX(), getY(): 对象的当前位置
  • isAlive(): 这个对象是否是活着的(即没有被俘)
  • getId(): 惟一 ID (跨越所有对象——骑士、农民和城堡)

IObject 父接口还有两个方便的方法。在设计战略时这些方法很有用、并可帮助您避免使用复杂的三角数学:

  • getDirectionTo() 计算到图中指定点最接近的方向。
  • getDistanceTo() 计算到图中指定点的距离。

IPeasant 接口
IPeasant 接口没有对 IObject 接口增加新的行为。可以用 Ruler 的 move() 方法移动农民,这会改变他们的位置。您用农民占据土地。农民的自动行为是占据他走过的任何土地。敌对的骑士可以通过移动俘获农民,不涉及体力计算。

ICastle 接口
ICastle 接口就像 IPeasant 接口,不对 IObject 接口增加新的行为。一个城堡的自动行为是创造更多的农民或者骑士。创造速度取决于拥有的土地数量。有关创造速度,请参阅侧栏 创造新农民和城堡。

IKnight 接口
IKnight 接口对 IObject 接口增加了一个名为 getStrength() 的方法。一个骑士在其体力值降低为零时被俘获。可以在战略中用 IKnight 接口的 getStrength() 方法避免损失骑士。有关骑士的体力计算的讨论请参阅本文前面 俘获游戏对象。

图 7 中的接口层次结构表示了模拟期间在世界中移动的游戏对象。不过,统治者不是游戏对象,并不在模拟世界中移动。IRuler 接口指定统治者的行为。

IRuler 接口
IRuler 接口不需要——并且没有——继承 IObject。图 8 显示了 IRuler 接口的继承层次。

图 8. IRuler 接口的继承层次

IRuler 接口指定了所有统治者实现的一般性行为。其中包括实现战略所需要的获取信息的方法:

  • getPeasants() 得到该统治者名下的所有农民。
  • getKnights() 得到该统治者名下的所有骑士。
  • getCastles() 得到该统治者名下的所有城堡。
  • getLandCount() 得到该统治者所拥有的土地方格数。
  • getPoints() 得到该统治者当前所赢得的分数。
  • getRulerName() 得到统治者的名字。
  • getSchoolName() 得到创造该统治者的团体名称。

Ruler 和 MyRuler 类
为了加强特定于游戏规则的行为,并且帮助实现 IRuler 接口,CodeRuler 提供了如 图 8 所示的 Ruler 类。这个类提供了大多数 IRuler 方法的默认实现。您要编写 MyRuler 类的内容,它必须继承 Ruler 类。您不需要、也不应该修改 Ruler 类。

模拟器的工作流程
从 CodeRuler 玩家的角度看,这个模拟器有下面的工作流程:

  1. 将初始游戏对象放到模拟世界中随机选择的王国位置
  2. 调用您实现的 initialize() 方法
  3. 每一回合调用您的 orderSubjects() 方法

getRulerName()getSchoolName() 方法不应当包含战略代码。模拟器可以在任何时候调用它们。

Ruler 提供了几个应当在自己的 MyRuler 实现中使用的关键操作方法:

  • move() 在世界中移动对象。
  • capture() 攻击俘获对手的游戏对象。

Ruler 还实现了几个会改变城堡的生产模式的方法。在默认情况下,城堡不断地制造农民。不过,可以使用这些方法告诉城堡要制造骑士:

  • createKnights() 告诉城堡制造骑士。
  • createPeasants() 告诉城堡制造农民。

最后,需要存在 Ruler 类的理由之一是为了在自己的 MyRuler 类中定义其他必须实现的抽象方法。模拟引擎在执行期间调用这些方法。

惟一必须编写的代码是在表 3 中所列方法的实现代码:

表 3. 所有 MyRuler 实现中的方法

方法 说明
getSchoolName() 返回一个有 25 个或者更少字符的字符串,它标识了您的小组或者团体。(CodeRuler 原来是为同事之间的竞争而设计的。)在游戏过程中确定统治者时将会用到它。例如,在 图 1 中,Simple Ruler 的 school name 是 IBM developerWorks。
getRulerName() 返回一个有 25 个或者更少的字符的字符串,它惟一地标识了统治者。例如,图 1 中的一个统治者名为 Simple Ruler。
initialize() 当第一次将统治者放到游戏中时,系统调用这个方法。在这里执行所有必需的初始化。初始化的时间限于一秒钟。计算机在初始化时可以完成的工作随着 CPU 速度和所使用的 Java VM 而变化,但是一秒钟对于大多数代码初始化任务是足够了。不要试图执行任何依赖慢速输入/输出的工作。
orderSubjects() 这是 CodeRuler 的核心。每回合系统调用一次这个方法。需要使用战略并告诉您的对象在这个方法中做什么。

Eclipse: 集成的王国开发环境
需要下载并安装 Eclipse IDE (版本 2.1 或者更新版本) 以运行 CodeRuler 模拟环境(请参阅 参考资料)。CodeRuler 作为插件集成到 Eclipse IDE 中,因而可以利用 Eclipse 的开发人员友好的特性。

安装 Eclipse 和 CodeRuler
要安装 Eclipse,将发布文档解压缩到一个目录中并运行 eclipse 可执行文件 (在 *nix 中是 eclipse,在 Win32 系统中是 eclipse.exe)。需要已安装 JDK/JRE 1.4.2 或更新版本。(强烈推荐版本 1.4.2,因为 CodeRuler 是在这个 VM 版本中开发和测试的。) 在安装了 Eclipse 后,下载 CodeRuler 引擎 (请参阅 参考资料)。要安装 CodeRuler,需要将 CodeRuler 发布文档解压缩到 <eclipse 安装目录>/plugins 目录。这会在 pugins 目录中创建一个 com.ibm.games 目录。启动或者重新启动 Eclipse,将会装载 CodeRuler 插件。现在就可以使用 CodeRuler 了。

创建自己的 CodeRuler 项目
需要在 Eclipse 中创建一个新项目以使用 CodeRuler。从主菜单中,选择 Windows|Preferences。会弹出一个对话框,如图 9 所示。

图 9. 创建新的 CodeRuler 项目

在左边的列表中选择 IBM Games,如图 9 所示。然后,从右边的 Game 列表中选择 CodeRuler。最后,单击 OK 以用模板创建一个新的 CodeRuler 项目。现在就可以编写自己的 CodeRuler 了。

在 IDE 左边的选项卡栏中,单击 Java Perspective 选项卡。图 10 表现了这个选项卡。

图 10. 在 Eclipse 中选择 Java perspective

展开 src 节点,默认包展现了 MyRuler.java 节点,如图 10 所示。双击 MyRuler.java 节点会在源代码编辑器中打开这个文件以进行编辑。必须在这里加入自己的代码。

编写第一个统治者
创建的第一个统治者很简单。它随机地移动所有的农民。清单 1 显示了这个统治者的代码,用粗体突出显示了添加的代码。

及时完成的重要性
在编写统治者代码时,要了解所遇到的时间约束。对于 initialize() 方法,只限于一秒钟,这对于所有没有输出/输出的初始化代码是相当多的时间了。如果花费了一秒以上的时间就会被中断(preempted)并可能只完成了部分初始化。在游戏的每一回合,orderSubjects() 方法限制为半秒钟。orderSubjects() 调用的输入参数可以让您知道在上一回合中使用了多少时间。如果超过了时间限制,就没有资格参加其余的比赛了。

清单 1. Simple Ruler 的 orderSubjects() 实现

import java.util.Random; ... protected Random rand = new Random(); public String getRulerName() { return "Simple Ruler"; } public String getSchoolName() { return "IBM developerWorks"; } public void orderSubjects(int lastMoveTime) { IPeasant[] peasants = getPeasants(); for (int i = 0; i < peasants.length; i++) { move(peasants[i], rand.nextInt(8) + 1); } } 

清单 1 中的代码用 java.util.Random 生成 1 到 8 之间的一个随机数。这个数决定了农民移动的方向。注意使用了 Ruler 类的 getPeasants() 方法获得所有农民的数组,使用了 move() 方法移动农民。

随机移动农民可以使他们占据土地。但是因为这个统治者并不试图俘获任何东西,所以代码不需要移动骑士。

CodeRuler “不要做的事情”列表
聪明的战略对于赢得游戏是很关键的,但是 CodeRuler 不鼓励使用 Java 语言功能绑架游戏引擎或者用其他不正当的方法取胜。代码 不应当

  • 定义构造函数
  • 使用初始化块
  • 创建线程
  • 创建进程
  • 写入文件
  • 使用 JDBC
  • 使用 Swing 或者 AWT 创建 GUI 组件
  • 访问网络或者其他类似的系统功能
  • 使用反射和内省发现并穿过模拟器内部结构

在所以公开比赛和锦标赛中,使用这些黑客技术的玩家会被取消比赛资格。一个定制的 Java SecurityManager 会抓获大部分这种企图。

第一场比赛的战斗
要试验第一场比赛,首先通过单击工具栏中的保存按钮或者选择菜单中的 File >Save 保存最新编辑的统治者。保存还会编译代码。在继续进行之前改正所有打字错误或者语法错误。

您会注意到 5 个特定于 CodeRuler 的图标按钮,如图 11 所示。

图 11. 在 Eclipse 工具栏中集成的 CodeRuler 按钮

表 4 说明了图 11 中从左到右的按钮的功能。

表 4. CodeRuler 按钮的功能

按钮 说明
Run against samples 用这个按钮与所选的示例统治者对抗以测试自己的统治者。
Debug against samples 用这个按钮与所选的示例统治者对抗以测试自己的统治者。以调试模式运行统治者,在所设置的中断点处停止。
Run against other teams 在提交了代码后,会下载其他小组的统治者。用这个按钮与其他小组的统治者对抗以测试自己的统治者。
Debug against other teams 在与其他小组的统治者对抗以测试自己的统治者时,以 IDE 的调试模式运行统治者。
Submit code 提交统治者。这还会下载以前其他小组提交的所有统治者的一个加密包。

在第一次试验中,您的第一个统治者将会只与示例统治者对抗。这意味着您将只使用第一个按钮,即 图 11 中突出显示的那个按钮。单击这个按钮时,CodeRuler 就会启动并装载您的统治者。您将有机会选择对手,如图 12 所示。

图 12. 选择比赛对手

试着增加一个 Do Nothing Ruler。开始比赛并观察农民是如何随机移动并占据土地的。可以容易地赢得这场比赛。

然后,试一下 Random Ruler。这个统治者的行为与您的统治者几乎一样。平均占有的土地大致相同。

如果与任何其他示例统治者对抗,那么您所创建的 Simple Ruler 很可能会输。大多数其他示例统治者会积极地俘获您的对象。现在应该在这个 Simple Ruler 中加入进攻能力了。

创建一个进攻性统治者
清单 2 显示了修改后的统治者代码,突出显示了增加的代码。

清单 2. 修改后的 Simple Ruler 实现,它积极获对手

 import com.ibm.ruler.*; import java.awt.Point; import java.util.Random; import java.util.Vector; public class MyRuler extends Ruler { public String getRulerName() { return "Simple Ruler"; } public String getSchoolName() { return "IBM developerWorks"; } public void initialize() { } protected Random rand = new Random(); protected Vector enemies = new Vector(); public void orderSubjects(int lastMoveTime) {  IPeasant[] peasants = getPeasants(); IKnight[] knights = getKnights(); for (int i = 0; i < peasants.length; i++) { move(peasants[i], rand.nextInt(8) + 1); }  enemies.clear(); IPeasant[] otherPeasants = World.getOtherPeasants(); IKnight[] otherKnights = World.getOtherKnights(); ICastle[] otherCastles = World.getOtherCastles(); for (int i=0; i<otherPeasants.length; i++) { enemies.add(otherPeasants[i]); } for (int i=0; i<otherKnights.length; i++ ){ enemies.add(otherKnights[i]); } for (int i=0; i<otherCastles.length; i++) { enemies.add(otherCastles[i]); }  int size = knights.length; for (int i = 0; i < size; i++) { IKnight curKnight = knights[i]; if (!enemies.isEmpty()) { IObject curEnemy = (IObject) enemies.remove(0); moveAndCapture(curKnight, curEnemy); } else break; } // of outter for  } public void moveAndCapture(IKnight knight, IObject enemy) { if ((enemy == null) || !enemy.isAlive()) return; // find the next position in the direction of the enemy int dir = knight.getDirectionTo(enemy.getX(), enemy.getY()); Point np = World.getPositionAfterMove(knight.getX(), knight.getY(), dir); if (np == null) return; if ((np.x == knight.getX()) && (np.y == knight.getY())) { move(knight, rand.nextInt(8) + 1); return; } // capture anything that is in our way IObject obj = World.getObjectAt(np.x, np.y); if ((obj != null) && (obj.getRuler()!= this)) capture(knight, dir); else move(knight, dir); } } 

万能的 World 对象
在开始编写大量的统治者编码之前,要保证花同样多的时间研究 World 对象的文档。这个对象包含许多静态方法,它们对于实现您的战略会很有用。例如,可以使用 getLandOwner() 找出谁拥有某个格子的土地、用 getObjectAt() 标识特定位置上的游戏对象、用 getOtherPeasants()getOtherKnights()getOtherCastles()getOtherRulers() 发现敌人。

您将体会到清单 2 中用绿色突出显示的代码的作用。

用红色突出显示的代码设置了一个包含在模拟世界中所有活着的敌方游戏对象的 Vector。注意使用 World 对象得到这个信息(即 World.getOtherPeasants())。

用蓝色突出显示的代码遍历您的所有骑士并使他们朝着活着的对方游戏对象移动。它还俘获所有可能遇到的敌对对象。 它使用 moveAndCapture() 方法移动和俘获。

moveAndCapture() 方法使特定的骑士移向特定的敌方对象。它使用 World 对象的 getPositionAfterMove() 方法确定骑士是否陷入困境,如果是,就进行随机的移动。它还使用 World 对象的 getObjectAt() 方法测试并俘获可能在其路上的所有敌方对象。

试着用这个新的 Simple Ruler 与一些示例统治者对抗。您将看到它对其中的很多统治者可以有不错的进展。当然,还有很大的改进余地。作为一个练习,您可以试着这样修改代码:

  • 命令您的城堡在骑士数量降低时生成骑士。
  • 更有效地为骑士指定目标。
  • 使农民更有效地占据土地。
  • 使农民躲避俘获。
  • 当农民和骑士的数量变低时,转为防御性生存战略。

结束语
由您来选择:从最简单的基于试探的机械式统治者到最复杂的、由统计游戏理论模型驱动的指挥官,CodeRulers 提供了所有可能性。就像在真实世界中一样,最复杂的战略和复杂的编码未必能保证胜利。事实上,一些冠军统治者使用了最简单、然而最精彩的游击战术。如果战略设计和 Java 开发是您的最爱,那么您应当试试 CodeRuler。

参考资料

  • 了解世界上历史最悠久、最大和最有声望的编程竞赛 ACM International Collegiate Programming Contest 的更多信息。
  • 从 IBM alphaWorks 下载最新版本的 CodeRuler 模拟引擎和相关的文档。
  • 请访问 eclipse.org 以得到最新版本的 Eclipse IDE、文档、邮件列表和社团新闻。
  • 查看另一个由与 CodeRuler 同样的模拟引擎支持的经典模拟游戏。 Code Rally 让您坐到一个汽车拉力赛的座位上!
  • 有关 alphaWorks 上另一个非常流行的战斗模拟游戏的情况,请访问 Robocode。这个活跃的 Robocode 玩家国际团体一定会给您带来挑战的。
  • 通过 Sing Li 的文章 “重锤痛击 Robocode!” (developerWorks,2002 年 1 月) 和 “Rock 'em, sock 'em Robocode: Round 2” (developerWorks,2002 年 5 月) 了解更多有关 Robocode 的信息。
  • 请访问 Developer Bookstore,获取技术书籍的完整列表,其中包括数百本 Java 相关 的书籍。
  • developerWorks Java 技术专区 上可以找到到数百篇 Java 技术文章。
  • 是否对无需通常的高成本入口点(entry point )或短期评估许可证的 IBM 测试产品感兴趣? developerWorks Subscription 为 WebSphere?、DB2?、Lotus?、Rational? 和 Tivoli? 产品提供了低成本的 12 个月单用户许可证,包括基于 Eclipse 的 WebSphere Studio IDE,用于开发、测试、评估和展示您的应用程序。

用 CodeRuler 征服中世纪王国相关推荐

  1. 如何提高自己的 Java 编程技能

    如何提高自己的 Java 编程技能 可以按照两条路线提高自己的技能:参加一个课程(为了认证或者只是为了学习)或自学(当然,还要通过编写代码进行实践).除了能够从有经验的开发人员那里获得知识之外,课程或 ...

  2. Creating a Font for Apps and Games with Glyphs 如何使用Glyphs为应用和游戏创建字体 Lynda课程中文字幕

    Creating a Font for Apps and Games with Glyphs 中文字幕 如何使用Glyphs为应用和游戏创建字体 中文字幕Creating a Font for App ...

  3. 游戏设计的艺术:一本透镜的书——第十五章 其中一种体验是故事

    这是一本游戏设计方面的好书 转自天之虹的博客:http://blog.sina.com.cn/jackiechueng 感谢天之虹的无私奉献 Word版可到本人的资源中下载 第十五章 其中一种体验是故 ...

  4. 工作三年后,我选择离开腾讯

    来源:微信公众号「LJ 说」,id:「LjNotes」. "你居然要从腾讯离职了!?" 这是身边朋友得知我要离开后的反应,似乎大家都难以理解这样的决定. 从行业环境来看,中国互联网 ...

  5. comeout(comeout)

    comeout有被动语态吗 没有.comeout是相当于不及物动词的动词短语,是不能用于被动语态的.再比如takeplace,breakout等,也是如此.如果对你有所帮助,请点击我回答下面的&quo ...

  6. 感官王国征服理性世界

    除了极个别的咆哮,穆里尼奥摇头.摇头.一直摇头,他所盼望的切尔西反击战,却就像切尔西的"死亡倒计时",90分钟,像最无情的流沙一样一点一滴,耗费干净. 切尔西根本没有机会,巴萨也没 ...

  7. 通证经济大局观(二十八):中世纪的主要组织

    现代民族国家产生于西方,孕育于中世纪.所以今天,我们先看看中世纪的西欧,活跃着哪些力量. 中国大一统王朝下,家庭.家族.帝国三种组织结构是层层嵌套的,家庭属于家族,家族属于帝国,帝国的官僚管到县一级, ...

  8. 沉没的王国---揭秘滇东自杞国(3)

    第五集 悲壮的抗蒙战争(上) 滇东自杞国,由于正确实施了"贸易立国,贩马兴邦"路线,还在中前期就已"独雄于诸蛮",一跃而为西南第一强国,从而开创了乌蛮民族的英雄 ...

  9. 3星|《一万年的爆发:文明如何加速人类进化》:那些拒绝承认欧洲征服和定居美洲过程中生物学差异的人,事实上也是在否认达尔文进化论...

    一万年的爆发(文明如何加速人类进化)(精) 作者在书中向<枪炮病菌与钢铁>叫板,全书风格也有点类似<枪炮病菌与钢铁>的旁征博引,不过程度稍逊.作者认为不同族群之间在基因上的差异 ...

最新文章

  1. cf1139D. Steps to One(dp)
  2. 为没有源码的DLL文件添加强名称
  3. vc++ 提取网页上的文字_用奢悦vc一般多久能反黑
  4. [Lintcode]102. Linked List Cycle/[Leetcode]
  5. linux计划任务一小时,linux,计划任务,每小时执行一次(共7篇).docx
  6. ubuntu启动virtualbox出错解决办法:RTR3InitEx failed with rc=-1912 (rc=-1912)
  7. Apache Spark源码剖析
  8. 摩尔吧 FPGA培训
  9. matlab newton插值法,Matlab程序Newton插值函数
  10. 旧版Wordpress的备案整改技巧
  11. Linux -- fflush函数
  12. Unity3D 动态加载资源方式
  13. Cocos2d-JS: 2.编译到安卓和iOS
  14. IBM ITS中国区总经理鞠立老师讲女性的职业规划与发展
  15. 分享20个无版权的高清无码图库站
  16. matlab怎么产生帕斯卡矩阵,MATLAB(一):矩阵基本操作
  17. Allegro使用 Z-Copy绘制 Rout Keepin
  18. 险些“B轮死”的小猪短租,如何穿越了融资生死线
  19. 车辆监控系统使用帮助
  20. 遥感与GIS在滑坡、泥石流风险普查中的实践技术应用

热门文章

  1. 电力电子技术总结-电力电子器件1
  2. 计算机安装xp蓝屏怎么办,谁知道电脑装XP中途蓝屏怎么处理?
  3. “避开”电视剧的腾讯视频为何要叫板“综艺”?
  4. 《C++程序设计语言》5.9_11输入读一系列的单词,使用quit作为输入的结束单词
  5. 视频爬虫js逆向——我搜不到密参怎么办
  6. 程序员需要研究厚黑学吗
  7. 【Python表白代码】独一无二的浪漫
  8. 计算机网络犯罪和一般犯罪的不同,网络犯罪有哪些类型
  9. MapboxGL系列(一)基础知识介绍
  10. 逆袭之旅DAY20.XIA.程序调试