# HG changeset patch # User Jaroslav Tulach # Date 1401046162 -7200 # Node ID e2c6e9a946f1789020b09f82661c4ef6ad88b8d7 # Parent 3bad49b62dfaa096c76320ab598c1a55072d3833 If possible, move the mine into just clicked square diff -r 3bad49b62dfa -r e2c6e9a946f1 minesweeper/src/main/java/org/apidesign/demo/minesweeper/MinesModel.java --- a/minesweeper/src/main/java/org/apidesign/demo/minesweeper/MinesModel.java Tue May 20 08:47:01 2014 +0200 +++ b/minesweeper/src/main/java/org/apidesign/demo/minesweeper/MinesModel.java Sun May 25 21:29:22 2014 +0200 @@ -108,6 +108,7 @@ @Function static void giveUp(Mines model) { showAllBombs(model, SquareType.EXPLOSION); + model.setState(GameState.LOST); } @ModelOperation static void init(Mines model, int width, int height, int mines) { @@ -237,23 +238,127 @@ if (model.getState() != GameState.IN_PROGRESS) { return; } - - switch (data.getState()) { - case UNKNOWN: - if (data.isMine()) { - showAllBombs(model, SquareType.EXPLOSION); - model.setState(GameState.LOST); - AudioClip oops = AudioClip.create("oops.wav"); - oops.play(); - } else { - AudioClip touch = AudioClip.create("move.mp3"); - touch.play(); - expandKnown(model, data); - model.computeMines(); + if (data.getState() != SquareType.UNKNOWN) { + return; + } + if (data.isMine()) { + Square fair = atLeastOnePlaceWhereBombCantBe(model); + if (fair == null) { + if (placeBombElseWhere(model, data)) { + cleanedUp(model, data); + return; } - break; + } + explosion(model); + } else { + Square takeFrom = tryStealBomb(model, data); + if (takeFrom != null) { + final Square fair = atLeastOnePlaceWhereBombCantBe(model); + if (fair != null) { + takeFrom.setMine(false); + data.setMine(true); + explosion(model); + return; + } + } + cleanedUp(model, data); } } + + private static void cleanedUp(Mines model, Square data) { + AudioClip touch = AudioClip.create("move.mp3"); + touch.play(); + expandKnown(model, data); + model.computeMines(); + } + + private static void explosion(Mines model) { + showAllBombs(model, SquareType.EXPLOSION); + model.setState(GameState.LOST); + AudioClip oops = AudioClip.create("oops.wav"); + oops.play(); + } + + private static Square tryStealBomb(Mines model, Square data) { + data.setMine(true); + final List rows = model.getRows(); + for (int y = 0; y < rows.size(); y++) { + final List columns = rows.get(y).getColumns(); + for (int x = 0; x < columns.size(); x++) { + Square sq = columns.get(x); + if (sq == data) { + continue; + } + if (sq.isMine()) { + sq.setMine(false); + final boolean ok = isConsistent(model); + sq.setMine(true); + if (ok) { + data.setMine(false); + return sq; + } + } + } + } + data.setMine(false); + return null; + } + + private static Square atLeastOnePlaceWhereBombCantBe(Mines model) { + final List rows = model.getRows(); + Square cantBe = null; + int discovered = 0; + for (int y = 0; y < rows.size(); y++) { + final List columns = rows.get(y).getColumns(); + for (int x = 0; x < columns.size(); x++) { + Square sq = columns.get(x); + if (sq.getState() == SquareType.UNKNOWN) { + if (!sq.isMine()) { + if (tryStealBomb(model, sq) == null) { + cantBe = sq; + } + } + } else { + discovered++; + } + } + } + + if (discovered > 5) { + return cantBe; + } + + return null; + } + + private static boolean placeBombElseWhere(Mines model, Square moveBomb) { + List ok = new ArrayList(); + moveBomb.setMine(false); + final List rows = model.getRows(); + for (int y = 0; y < rows.size(); y++) { + final List columns = rows.get(y).getColumns(); + for (int x = 0; x < columns.size(); x++) { + Square sq = columns.get(x); + if (sq == moveBomb || sq.isMine() || sq.getState().isVisible()) { + continue; + } + sq.setMine(true); + if (isConsistent(model)) { + ok.add(sq); + } + sq.setMine(false); + } + } + if (ok.isEmpty()) { + moveBomb.setMine(true); + return false; + } else { + int r = new Random().nextInt(ok.size()); + ok.get(r).setMine(true); + return true; + } + } + private static void expandKnown(Mines model, Square data) { final List rows = model.getRows(); for (int y = 0; y < rows.size(); y++) { @@ -277,15 +382,7 @@ } final Square sq = columns.get(x); if (sq.getState() == SquareType.UNKNOWN) { - int around = - minesAt(model, x - 1, y - 1) + - minesAt(model, x - 1, y) + - minesAt(model, x - 1, y + 1) + - minesAt(model, x , y - 1) + - minesAt(model, x, y + 1) + - minesAt(model, x + 1, y - 1) + - minesAt(model, x + 1, y) + - minesAt(model, x + 1, y + 1); + int around = around(model, x, y); final SquareType t = SquareType.valueOf("N_" + around); sq.setState(t); if (t == SquareType.N_0) { @@ -300,6 +397,18 @@ } } } + + private static int around(Mines model, int x, int y) { + return + minesAt(model, x - 1, y - 1) + + minesAt(model, x - 1, y) + + minesAt(model, x - 1, y + 1) + + minesAt(model, x , y - 1) + + minesAt(model, x, y + 1) + + minesAt(model, x + 1, y - 1) + + minesAt(model, x + 1, y) + + minesAt(model, x + 1, y + 1); + } private static int minesAt(Mines model, int x, int y) { if (y < 0 || y >= model.getRows().size()) { @@ -312,6 +421,22 @@ Square sq = columns.get(x); return sq.isMine() ? 1 : 0; } + + private static boolean isConsistent(Mines m) { + for (int row = 0; row < m.getRows().size(); row++) { + Row r = m.getRows().get(row); + for (int col = 0; col < r.getColumns().size(); col++) { + Square sq = r.getColumns().get(col); + if (sq.getState().isVisible()) { + int around = around(m, col, row); + if (around != sq.getState().ordinal()) { + return false; + } + } + } + } + return true; + } /** * Called when page is ready