1.1 --- a/minesweeper/src/main/java/org/apidesign/demo/minesweeper/MinesModel.java Tue May 20 08:47:01 2014 +0200
1.2 +++ b/minesweeper/src/main/java/org/apidesign/demo/minesweeper/MinesModel.java Sun May 25 21:29:22 2014 +0200
1.3 @@ -108,6 +108,7 @@
1.4
1.5 @Function static void giveUp(Mines model) {
1.6 showAllBombs(model, SquareType.EXPLOSION);
1.7 + model.setState(GameState.LOST);
1.8 }
1.9
1.10 @ModelOperation static void init(Mines model, int width, int height, int mines) {
1.11 @@ -237,23 +238,127 @@
1.12 if (model.getState() != GameState.IN_PROGRESS) {
1.13 return;
1.14 }
1.15 -
1.16 - switch (data.getState()) {
1.17 - case UNKNOWN:
1.18 - if (data.isMine()) {
1.19 - showAllBombs(model, SquareType.EXPLOSION);
1.20 - model.setState(GameState.LOST);
1.21 - AudioClip oops = AudioClip.create("oops.wav");
1.22 - oops.play();
1.23 - } else {
1.24 - AudioClip touch = AudioClip.create("move.mp3");
1.25 - touch.play();
1.26 - expandKnown(model, data);
1.27 - model.computeMines();
1.28 + if (data.getState() != SquareType.UNKNOWN) {
1.29 + return;
1.30 + }
1.31 + if (data.isMine()) {
1.32 + Square fair = atLeastOnePlaceWhereBombCantBe(model);
1.33 + if (fair == null) {
1.34 + if (placeBombElseWhere(model, data)) {
1.35 + cleanedUp(model, data);
1.36 + return;
1.37 }
1.38 - break;
1.39 + }
1.40 + explosion(model);
1.41 + } else {
1.42 + Square takeFrom = tryStealBomb(model, data);
1.43 + if (takeFrom != null) {
1.44 + final Square fair = atLeastOnePlaceWhereBombCantBe(model);
1.45 + if (fair != null) {
1.46 + takeFrom.setMine(false);
1.47 + data.setMine(true);
1.48 + explosion(model);
1.49 + return;
1.50 + }
1.51 + }
1.52 + cleanedUp(model, data);
1.53 }
1.54 }
1.55 +
1.56 + private static void cleanedUp(Mines model, Square data) {
1.57 + AudioClip touch = AudioClip.create("move.mp3");
1.58 + touch.play();
1.59 + expandKnown(model, data);
1.60 + model.computeMines();
1.61 + }
1.62 +
1.63 + private static void explosion(Mines model) {
1.64 + showAllBombs(model, SquareType.EXPLOSION);
1.65 + model.setState(GameState.LOST);
1.66 + AudioClip oops = AudioClip.create("oops.wav");
1.67 + oops.play();
1.68 + }
1.69 +
1.70 + private static Square tryStealBomb(Mines model, Square data) {
1.71 + data.setMine(true);
1.72 + final List<Row> rows = model.getRows();
1.73 + for (int y = 0; y < rows.size(); y++) {
1.74 + final List<Square> columns = rows.get(y).getColumns();
1.75 + for (int x = 0; x < columns.size(); x++) {
1.76 + Square sq = columns.get(x);
1.77 + if (sq == data) {
1.78 + continue;
1.79 + }
1.80 + if (sq.isMine()) {
1.81 + sq.setMine(false);
1.82 + final boolean ok = isConsistent(model);
1.83 + sq.setMine(true);
1.84 + if (ok) {
1.85 + data.setMine(false);
1.86 + return sq;
1.87 + }
1.88 + }
1.89 + }
1.90 + }
1.91 + data.setMine(false);
1.92 + return null;
1.93 + }
1.94 +
1.95 + private static Square atLeastOnePlaceWhereBombCantBe(Mines model) {
1.96 + final List<Row> rows = model.getRows();
1.97 + Square cantBe = null;
1.98 + int discovered = 0;
1.99 + for (int y = 0; y < rows.size(); y++) {
1.100 + final List<Square> columns = rows.get(y).getColumns();
1.101 + for (int x = 0; x < columns.size(); x++) {
1.102 + Square sq = columns.get(x);
1.103 + if (sq.getState() == SquareType.UNKNOWN) {
1.104 + if (!sq.isMine()) {
1.105 + if (tryStealBomb(model, sq) == null) {
1.106 + cantBe = sq;
1.107 + }
1.108 + }
1.109 + } else {
1.110 + discovered++;
1.111 + }
1.112 + }
1.113 + }
1.114 +
1.115 + if (discovered > 5) {
1.116 + return cantBe;
1.117 + }
1.118 +
1.119 + return null;
1.120 + }
1.121 +
1.122 + private static boolean placeBombElseWhere(Mines model, Square moveBomb) {
1.123 + List<Square> ok = new ArrayList<Square>();
1.124 + moveBomb.setMine(false);
1.125 + final List<Row> rows = model.getRows();
1.126 + for (int y = 0; y < rows.size(); y++) {
1.127 + final List<Square> columns = rows.get(y).getColumns();
1.128 + for (int x = 0; x < columns.size(); x++) {
1.129 + Square sq = columns.get(x);
1.130 + if (sq == moveBomb || sq.isMine() || sq.getState().isVisible()) {
1.131 + continue;
1.132 + }
1.133 + sq.setMine(true);
1.134 + if (isConsistent(model)) {
1.135 + ok.add(sq);
1.136 + }
1.137 + sq.setMine(false);
1.138 + }
1.139 + }
1.140 + if (ok.isEmpty()) {
1.141 + moveBomb.setMine(true);
1.142 + return false;
1.143 + } else {
1.144 + int r = new Random().nextInt(ok.size());
1.145 + ok.get(r).setMine(true);
1.146 + return true;
1.147 + }
1.148 + }
1.149 +
1.150 private static void expandKnown(Mines model, Square data) {
1.151 final List<Row> rows = model.getRows();
1.152 for (int y = 0; y < rows.size(); y++) {
1.153 @@ -277,15 +382,7 @@
1.154 }
1.155 final Square sq = columns.get(x);
1.156 if (sq.getState() == SquareType.UNKNOWN) {
1.157 - int around =
1.158 - minesAt(model, x - 1, y - 1) +
1.159 - minesAt(model, x - 1, y) +
1.160 - minesAt(model, x - 1, y + 1) +
1.161 - minesAt(model, x , y - 1) +
1.162 - minesAt(model, x, y + 1) +
1.163 - minesAt(model, x + 1, y - 1) +
1.164 - minesAt(model, x + 1, y) +
1.165 - minesAt(model, x + 1, y + 1);
1.166 + int around = around(model, x, y);
1.167 final SquareType t = SquareType.valueOf("N_" + around);
1.168 sq.setState(t);
1.169 if (t == SquareType.N_0) {
1.170 @@ -300,6 +397,18 @@
1.171 }
1.172 }
1.173 }
1.174 +
1.175 + private static int around(Mines model, int x, int y) {
1.176 + return
1.177 + minesAt(model, x - 1, y - 1) +
1.178 + minesAt(model, x - 1, y) +
1.179 + minesAt(model, x - 1, y + 1) +
1.180 + minesAt(model, x , y - 1) +
1.181 + minesAt(model, x, y + 1) +
1.182 + minesAt(model, x + 1, y - 1) +
1.183 + minesAt(model, x + 1, y) +
1.184 + minesAt(model, x + 1, y + 1);
1.185 + }
1.186
1.187 private static int minesAt(Mines model, int x, int y) {
1.188 if (y < 0 || y >= model.getRows().size()) {
1.189 @@ -312,6 +421,22 @@
1.190 Square sq = columns.get(x);
1.191 return sq.isMine() ? 1 : 0;
1.192 }
1.193 +
1.194 + private static boolean isConsistent(Mines m) {
1.195 + for (int row = 0; row < m.getRows().size(); row++) {
1.196 + Row r = m.getRows().get(row);
1.197 + for (int col = 0; col < r.getColumns().size(); col++) {
1.198 + Square sq = r.getColumns().get(col);
1.199 + if (sq.getState().isVisible()) {
1.200 + int around = around(m, col, row);
1.201 + if (around != sq.getState().ordinal()) {
1.202 + return false;
1.203 + }
1.204 + }
1.205 + }
1.206 + }
1.207 + return true;
1.208 + }
1.209
1.210 /**
1.211 * Called when page is ready