Память с завихрением

Описание

Это хорошая старая игра Память с завихрением: каждый раз, когда вы выбираете пару неправильная , эти две плитки, которые вы выбрали , переключат свое местоположение . Поэтому иногда вы можете подумать, что плитка находится в одном месте, когда на самом деле ... она переместилась. И может показаться, что вы понятия не имеете, где это сейчас.

Автор этой игры не несет ответственности за любые сломанные клавиатуры, экраны и /или мыши.

Я использую версию GWT, совместимую с полу-Java8 . Он не поддерживает Stream API.

Где играть?

Здесь можно сыграть здесь: http://www.zomis.net/Просмотр Кода /память /MemoryGWT.html

Описание класса

  • MemoryGWT.java, MemoryGWT.html: Главная точка входа GWT.
  • MemoryGWT.css: просто какой-то простой CSS.
  • MemoryBoard.java: класс представления, используемый для основной платы памяти.
  • FieldView.java: представление для каждого фрагмента.
  • ListUtils.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

Unobfuscated скомпилированный Javascript доступен здесь: http://www.zomis.net/codereview /память /memorygwt /

31 голос | спросил Simon Forsberg 19 +04002014-10-19T22:39:21+04:00312014bEurope/MoscowSun, 19 Oct 2014 22:39:21 +0400 2014, 22:39:21

4 ответа


10

ДА

Игра чистая.

Как и большинство программного обеспечения, игра становится хитер всякий раз, когда имеется отладчик.

В этом случае я загрузил FireBug в Firefox и узнал несколько трюков. Вот как игра чит.

Обратите внимание, что это основано на игре с вашего источника здесь:

Процесс

  1. Включить FireBug
  2. Загрузите игру (нажмите ссылку)
  3. На вкладке «Скрипты» в Firebug выберите script[9] и прокрутите до строки 273. Это код JavaScript, используемый для заполнения вашей начальной сетки ....:

    Какие скрипты для отладки

  4. Включить точку прерывания около этой строки

  5. Нажмите f5 , чтобы обновить страницу. Это должно привести к частичному загрузке страницы, и точка останова будет там, где вы ее установили ....
  6. Затем на вкладке «Смотреть» мы можем проверить массив ints, который представляет собой «перетасованную» коллекцию значений ячеек, с которой инициализируется сетка. Каждый член массива документирует точное значение, которое помещается в каждом месте. Это можно использовать для выбора точных пар без ошибок.

    Значения ячеек

  7. Profit?

Примечания

Мне потребовалось немного времени, чтобы точно определить, где хранится контент GWT, о .... 30 минут, чтобы найти FireBug, установить, узнать, как он работает, отследить, куда идти, понять, как GWT загружает скрипты из массива текста, который он загружает отдельно, и т. д.

Человек, который больше знаком с JavaScript, модель исполнения в браузере и инструменты отладки, вероятно, могли бы сделать это намного быстрее.

Обфусканные базы кода сделают это более сложным, но концепция все равно будет одинаковой.

ответил rolfl 19 FriEurope/Moscow2014-12-19T08:33:58+03:00Europe/Moscow12bEurope/MoscowFri, 19 Dec 2014 08:33:58 +0300 2014, 08:33:58
12

Хм, очень приятно! Хорошее упражнение для памяти (несмотря на бит, мм, садистский поворот), и код красиво написан и очень трудно выбрать: что может быть окончательным окончательным, аргументы проверяются, нет магических чисел или дублированных строковых литералов, хорошо отформатированы и понятны, понятны. Я заметил, что вы выбрали последний элемент из перетасованного списка, и даже HTML передает валидатор w3c.

Как небольшая оптимизация, я бы рекомендовал удалить прослушиватели кликов из найденных фрагментов.

Объект Random используется только один раз, поэтому он может также быть локальной переменной, или еще лучше, только что встроенный в заявление, в котором вы его используете. Позже, если вы хотите сделать это testable, вы можете рассмотреть возможность добавления его в качестве аргумента конструктора. В любом случае, в текущем коде нет видимых причин сделать это полем-членом.

Хотя это умный, чтобы выскочить из элементов 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 вместо этого.

Простое усовершенствование юзабилити, которое может сделать игру намного более захватывающей, добавляет количество проб. Таким образом, я буду знать, улучшаюсь ли я или нет, иначе сложно сказать. Дальнейшее построение этого, история десятков недавно завершенных игр также будет отличной.

ответил janos 19 +04002014-10-19T23:04:30+04:00312014bEurope/MoscowSun, 19 Oct 2014 23:04:30 +0400 2014, 23:04:30
9

Выводить отрицательные выражения if

Я бы включил инструкции if в коде следующим образом:

if (!condition) {
    // long block of code
} else {
    // short block of code
}

У вас есть это дважды: previousClicked != null и !same.

Я бы сделал это по двум причинам: легче читать if(condition), чем if(!condition), а потому, что более короткий код находится наверху также становится легче увидеть, какие операторы else завершают инструкции if, когда у вас есть вложенные операторы.

Юзабилити

Я бы добавил функциональность, которая сразу скрывает два открытых фрагмента, если пользователь щелкает в любом месте, вместо того, чтобы игнорировать щелчок и позволить таймеру закончить. Может быть, я просто нетерпелив, но мне очень неприятно ждать таймера.

setWidget

  

grid.setWidget (y, x, view);

Должно ли это быть grid.setWidget(x, y, view);?

  

Можно ли обмануть, отладив переменные Javascript, чтобы заранее определить значения плиток?

Это было бы намного легче протестировать с помощью не обфусканного кода. Если вы измените свой пример, я уверен, что кто-то попытается обмануть его.

ответил tim 20 +04002014-10-20T00:05:46+04:00312014bEurope/MoscowMon, 20 Oct 2014 00:05:46 +0400 2014, 00:05:46
2

Я уверен, что можно найти нужные файлы 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, избавившись от отрицания.

После того как я избавился от отрицания, ясно, что мне не нужна логическая переменная same, потому что она используется только один раз и отбрасывается, поэтому я просто бросаю это равенство в оператор if .

Это оставляет нам:

private void clicked(FieldView view) {
    if (view == previousClicked || timerRunning) {
        return;
    }
    if (previousClicked != null) {
        boolean same = previousClicked.getValue() == view.getValue();
        view.showValue();
        if (previousClicked.getValue() == view.getValue()) {
            previousClicked = null;
        } else {
            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 {
        view.showValue();
        previousClicked = view;
    }
}

Я также использовал правильную привязку к операторам if /else. Мне нравится делать это так и иногда желать, чтобы стандарты C # позволяли мне делать это одинаково. ( shh никому не говорила, что я сказал, что )

ответил Malachi 17 WedEurope/Moscow2014-12-17T18:23:59+03:00Europe/Moscow12bEurope/MoscowWed, 17 Dec 2014 18:23:59 +0300 2014, 18:23:59

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132