If possible, move the mine into just clicked square
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 25 May 2014 21:29:22 +0200
changeset 152e2c6e9a946f1
parent 151 3bad49b62dfa
child 153 6d2eb47e966b
child 167 0d7d0ad381e2
If possible, move the mine into just clicked square
minesweeper/src/main/java/org/apidesign/demo/minesweeper/MinesModel.java
     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