这是一款不错的老游戏,但有一点点记忆:每次选择错误的对时,您选择的两个图块将切换它们的位置。因此,有时您可能会认为某个瓷砖实际上在某个位置,但是它已经移动了。并且可能感觉好像您不知道它在哪里。
该游戏的作者对键盘,屏幕和/或鼠标设备的任何损坏概不负责。
我正在使用半Java8兼容版本的GWT。它不支持Stream API。
在哪里玩?
可以在这里玩游戏:http://www.zomis.net/codereview/memory/MemoryGWT .html
类摘要
MemoryGWT.java,MemoryGWT.html:主要的GWT入口点。
MemoryGWT.css:只是一些简单的CSS。
MemoryBoard.java:用于主内存板的视图类
FieldView.java:每个图块的视图。 。仅包含
shuffle
的实现,因为Collections.shuffle
在GWT中不起作用。代码
FieldView.java:(39行,760字节)
public class FieldView implements IsWidget {
private static final String HIDDEN_LABEL = "";
private final Button widget;
private int value;
public FieldView(int value) {
this.value = value;
widget = new Button(HIDDEN_LABEL);
widget.setStyleName("game-button", true);
}
@Override
public Button asWidget() {
return widget;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public void showValue() {
widget.setText(String.valueOf(value));
}
public void hideValue() {
widget.setText(HIDDEN_LABEL);
}
}
MemoryBoard.java:(86行,2081字节)
public class MemoryBoard implements IsWidget {
private final Grid grid;
private final Random random = new Random();
private FieldView previousClicked;
private boolean timerRunning;
public MemoryBoard(int width, int height) {
if ((width * height) % 2 != 0) {
throw new IllegalArgumentException("width * height must be an even number");
}
grid = new Grid(height, width);
grid.setStyleName("game");
List<Integer> ints = new ArrayList<>();
for (int i = 0; i < width * height / 2; i++) {
ints.add(i);
ints.add(i);
}
ListUtils.shuffle(ints, random);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int value = ints.remove(ints.size() - 1);
FieldView view = new FieldView(value);
view.asWidget().addClickHandler(e -> clicked(view));
grid.setWidget(y, x, view);
}
}
}
private void clicked(FieldView view) {
if (view == previousClicked) {
return;
}
if (timerRunning) {
return;
}
if (previousClicked != null) {
boolean same = previousClicked.getValue() == view.getValue();
view.showValue();
if (!same) {
Timer timer = new Timer() {
@Override
public void run() {
// switch the two values
int previous = previousClicked.getValue();
previousClicked.setValue(view.getValue());
view.setValue(previous);
view.hideValue();
previousClicked.hideValue();
previousClicked = null;
timerRunning = false;
}
};
timerRunning = true;
timer.schedule(2000);
}
else {
previousClicked = null;
}
}
else {
view.showValue();
previousClicked = view;
}
}
@Override
public Widget asWidget() {
return grid;
}
}
MemoryGWT.java:(15行,345字节)
public class MemoryGWT implements EntryPoint {
@Override
public void onModuleLoad() {
final MemoryBoard memory = new MemoryBoard(6, 6);
RootPanel.get("gameContainer").add(memory);
}
}
MemoryGWT.css
.game-button {
width: 42px;
height: 42px;
}
MemoryGWT.html
<!doctype html>
<!-- The DOCTYPE declaration above will set the -->
<!-- browser's rendering engine into -->
<!-- "Standards Mode". Replacing this declaration -->
<!-- with a "Quirks Mode" doctype is not supported. -->
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link type="text/css" rel="stylesheet" href="MemoryGWT.css">
<title>Memory Extreme</title>
<script type="text/javascript" src="memorygwt/memorygwt.nocache.js"></script>
</head>
<body>
<h1>Memory</h1>
<p>Good old memory with a twist: When you have picked a non-matching pair, the two tiles you have chosen switch.</p>
<!-- OPTIONAL: include this if you want history support -->
<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
<noscript>
<div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
Your web browser must have JavaScript enabled
in order for this application to display correctly.
</div>
</noscript>
<div id="gameContainer"></div>
</body>
</html>
问题
我主要关心的是:是否可以通过调试Javascript变量来预先弄清楚瓦片的值来作弊?我正在考虑制造更多GWT物品,但是如果可能作弊,那么我将不得不改变制造方式(例如,在实际行动之前动态生成物品)。
除此之外:我如何使用GWT?我不是很关注HTML和CSS内容,只是为了完整性而将它们包括在内。
未混淆的Javascript
可在此处找到未混淆的已编译Javascript:http://www.zomis.net/codereview/memory/memorygwt/
#1 楼
是游戏是可作弊的。
像大多数软件一样,只要有调试器,游戏就可作弊。
在这种情况下,我已经在Firefox中加载了FireBug,并学到了一些技巧。以下是游戏的作弊方式。
请注意,这是基于您在以下位置从您的来源玩的游戏:
可以在此处进行游戏:http://www.zomis.net/ codereview / memory / MemoryGWT.html
进程
启用FireBug
加载游戏(单击链接)
在Firebug的“脚本”选项卡中,选择
script[9]
并滚动到第273行。这是用于填充初始网格的JavaScript代码....:< br q 在该行附近启用断点
按f5刷新页面。这会导致页面部分加载,并且断点将在您设置的地方....
然后,在“监视”选项卡中,我们可以检查
ints
数组,这是初始化网格时单元格值的“混洗”集合。数组中的每个成员都记录了放置在每个位置的精确值。可以用来选择精确对,而不必担心错误。利润?
注意事项
它我花了一点时间来精确地查找GWT内容的存储位置,大约..... 30分钟找到了FireBug,安装,学习了它的工作原理,追踪了事情的进展,了解了GWT如何从一系列数组中加载脚本分别加载的文本等。
更熟悉JavaScript,浏览器中的执行模型和调试工具的人可能会更快地完成它。难度更大,但概念仍然相同。
#2 楼
嗯,非常好!记忆力十足的练习(尽管有一点点,有点儿,虐待狂),编写的代码很好,很难挑选:最终的可以是最终的,
参数经过验证,没有魔术数字或重复的字符串文字,格式正确,清晰易懂。我注意到您从改组后的列表中选择了最后一个元素,甚至HTML都通过了w3c验证程序。
Random
对象仅使用一次,因此它也可以是局部变量,或者更好,只需将其嵌入到使用它的语句中即可。您想使其可测试,可以考虑将其添加为构造函数参数。无论如何,在当前代码中没有明显的理由使其成为成员字段。尽管从末尾弹出
ints
的元素很聪明,完全可以:for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int value = ints.get(x * width + height);
尽管从理论上讲这是“高效的”,但在您的用例中,这实际上可以忽略不计。
您可以很好地使用短而甜美且效率低下的
ints.remove(0)
,仍然没有实际的区别。
较小的可用性注意事项:存储卡上的数字从0开始,
在示例页面上的范围是0到17。
对极客来说很好,但是普通人可能会期望使用1-18。令人上瘾的是增加了许多试验。这样,我就会知道自己是否有所好转,否则很难说。在此基础上,最近完成的游戏的得分历史也将是伟大的。
评论
\ $ \ begingroup \ $
您关于从匹配的图块中删除点击侦听器的建议不仅是“次要优化”,而且还解决了一个错误;)
\ $ \ endgroup \ $
–西蒙·福斯伯格
14-10-19在19:08
\ $ \ begingroup \ $
我认为普通人根本不会指望记忆游戏中的数字,而是美好的图画。因此,我认为以0开头的数字比较合适。
\ $ \ endgroup \ $
–蒂姆
14-10-19在19:53
#3 楼
切换否定的if语句我将在代码中切换if语句,如下所示:
我这样做有两个原因:读
previousClicked != null
比!same
容易,并且因为较短的代码位于顶部,所以嵌套后if(condition)
语句完成的内容也变得更容易语句。可用性
我将添加一项功能,如果用户单击任意位置,则可以立即隐藏两个打开的图块,而不是忽略单击并让计时器结束。可能只是我不耐烦,但是让我等待计时器非常烦人。
,view);
应该是
if(!condition)
吗? 是否可以通过调试Javascript变量来预先弄清楚瓦片的值来作弊?使用非混淆代码更容易测试。如果您更改示例,我敢肯定有人会尝试作弊。
评论
\ $ \ begingroup \ $
关于grid.setWidget:不,应该那样。 (我知道,感觉很奇怪...)
\ $ \ endgroup \ $
–西蒙·福斯伯格
14-10-19在20:08
\ $ \ begingroup \ $
至于混淆代码:我也可以提供非混淆版本(稍后提供),但是我将在诸如此类的任何游戏中实际使用的是混淆代码。
\ $ \ endgroup \ $
–西蒙·福斯伯格
14-10-19在20:11
#4 楼
我确信可以找到正确的Javascript文件并使用Cache重新创建数字网格,然后再实际单击任何游戏作品,但是,如果有人真的花时间欺骗了异常的硬盘游戏,那是很认真的,并且花时间找到文件,然后通读它们,甚至构建一个解析器将其弄清楚.....真的有区别吗?你在卖这个游戏吗?您打算从这款游戏中赚钱吗?
如果您想证明它是傻瓜,那么应该使用服务器端变量来保存数字格,以保持秘密,除非您“翻过来”游戏来查看它是什么。
我将对此代码进行一些更改
private void clicked(FieldView view) {
if (view == previousClicked) {
return;
}
if (timerRunning) {
return;
}
if (previousClicked != null) {
boolean same = previousClicked.getValue() == view.getValue();
view.showValue();
if (!same) {
Timer timer = new Timer() {
@Override
public void run() {
// switch the two values
int previous = previousClicked.getValue();
previousClicked.setValue(view.getValue());
view.setValue(previous);
view.hideValue();
previousClicked.hideValue();
previousClicked = null;
timerRunning = false;
}
};
timerRunning = true;
timer.schedule(2000);
}
else {
previousClicked = null;
}
}
else {
view.showValue();
previousClicked = view;
}
}
我将使用以下方法组合前两个if语句一个“或”运算符,然后将guard子句移至if语句,以消除否定。因为它仅使用一次并被丢弃,所以我只将等式放入if语句中。 >我还在if / else语句周围使用了适当的括号。我喜欢那样做,有时希望C#标准可以让我以同样的方式做。 (嘘,不要告诉任何人我说过的话)
评论
\ $ \ begingroup \ $
谢谢您的回答,但是您的回答并没有触及我最初悬赏的目标:我仍在通过调试Javascript变量来寻找有关游戏是否“可玩”的答案
\ $ \ endgroup \ $
–西蒙·福斯伯格
2014-12-17 15:48
\ $ \ begingroup \ $
不,我不出售这款游戏。但是我正在考虑用GWT重写我的扫雷游戏-这就是为什么我想知道它是否可作弊。我知道使用服务器端是使其完全无法加热的唯一方法,我正在为Minesweeper使用服务器,但我也希望拥有一个独立的仅客户端版本。
\ $ \ endgroup \ $
–西蒙·福斯伯格
2014-12-18 15:58
评论
太神奇了---几年前,我在学习编程时创建了一个完全相同的游戏名称。 vivatropolis.org/istvan/MemoryTwist.xhtml建议轻微改善可用性。选择两个磁贴后,必须等待单击下两个磁贴。单击第三个磁贴应隐藏选定的两个并显示第三个。这样可以加快游戏速度。