|
@@ -3,8 +3,13 @@ package eu.tankernn.mines;
|
|
|
import java.io.FileNotFoundException;
|
|
|
import java.text.DecimalFormat;
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Collections;
|
|
|
import java.util.List;
|
|
|
-import java.util.Random;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+import java.util.stream.IntStream;
|
|
|
+import java.util.stream.Stream;
|
|
|
|
|
|
import org.lwjgl.Sys;
|
|
|
import org.lwjgl.input.Keyboard;
|
|
@@ -24,12 +29,11 @@ import eu.tankernn.mines.Tile.TileState;
|
|
|
|
|
|
public class Mines extends TankernnGame {
|
|
|
public static final String GAME_NAME = "Minesweeper";
|
|
|
- public static final Pos[] DEFAULT_PATTERN = { new Pos(0, 1), new Pos(0, -1), new Pos(1, 0), new Pos(1, 1), new Pos(1, -1),
|
|
|
- new Pos(-1, 0), new Pos(-1, -1), new Pos(-1, 1) };
|
|
|
- public static Settings DEFAULT_SETTINGS = new Settings(DEFAULT_PATTERN, 9, 9, 20);
|
|
|
+ public static final Pos[] DEFAULT_PATTERN = { new Pos(0, 1), new Pos(0, -1), new Pos(1, 0), new Pos(1, 1),
|
|
|
+ new Pos(1, -1), new Pos(-1, 0), new Pos(-1, -1), new Pos(-1, 1) };
|
|
|
+ public static Settings DEFAULT_SETTINGS = new Settings(Arrays.asList(DEFAULT_PATTERN), 9, 9, 10);
|
|
|
|
|
|
// Utility
|
|
|
- private Random rand = new Random();
|
|
|
private DecimalFormat format = new DecimalFormat("0.000 sec");
|
|
|
private GuiRenderer renderer;
|
|
|
private SettingsEditor editor;
|
|
@@ -39,7 +43,7 @@ public class Mines extends TankernnGame {
|
|
|
private Texture[] checked;
|
|
|
private GUIText timeText;
|
|
|
private int tileWidth, tileHeight;
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Next game settings.
|
|
|
*/
|
|
@@ -51,7 +55,7 @@ public class Mines extends TankernnGame {
|
|
|
|
|
|
// Game state
|
|
|
private long startTime;
|
|
|
- private Tile[][] tiles;
|
|
|
+ private Map<Pos, Tile> tiles;
|
|
|
private int hiddenTiles;
|
|
|
private boolean running = false;
|
|
|
private boolean justClicked = false;
|
|
@@ -76,7 +80,7 @@ public class Mines extends TankernnGame {
|
|
|
} catch (FileNotFoundException e) {
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
setSettings(DEFAULT_SETTINGS);
|
|
|
startGame();
|
|
|
}
|
|
@@ -99,28 +103,15 @@ public class Mines extends TankernnGame {
|
|
|
running = true;
|
|
|
}
|
|
|
|
|
|
- private Tile[][] generateBoard(int width, int height, int mines) {
|
|
|
- if (width * height < mines) {
|
|
|
- throw new IllegalArgumentException("The mines will not fit on the board.");
|
|
|
- }
|
|
|
-
|
|
|
- List<Pos> minePositions = new ArrayList<Pos>();
|
|
|
- for (int i = 0; i < mines; i++) {
|
|
|
- Pos p;
|
|
|
- do {
|
|
|
- p = new Pos(rand.nextInt(width), rand.nextInt(height));
|
|
|
- } while (minePositions.contains(p));
|
|
|
- minePositions.add(p);
|
|
|
- }
|
|
|
+ private Map<Pos, Tile> generateBoard(int width, int height, int mines) {
|
|
|
+ assert width * height < mines;
|
|
|
|
|
|
- tiles = new Tile[width][height];
|
|
|
+ List<Pos> minePositions = new ArrayList<Pos>(allPositions(width, height).collect(Collectors.toList()));
|
|
|
+ Collections.shuffle(minePositions);
|
|
|
+ minePositions.subList(0, minePositions.size() - mines).clear();
|
|
|
|
|
|
- for (int y = 0; y < height; y++) {
|
|
|
- for (int x = 0; x < width; x++) {
|
|
|
- tiles[x][y] = new Tile(minePositions.contains(new Pos(x, y)), new Pos(x, y));
|
|
|
- }
|
|
|
- }
|
|
|
- return tiles;
|
|
|
+ return allPositions(width, height).map(p -> new Tile(minePositions.contains(p), p))
|
|
|
+ .collect(Collectors.toMap(t -> t.pos, t -> t));
|
|
|
}
|
|
|
|
|
|
public void check(Tile tile) {
|
|
@@ -131,40 +122,29 @@ public class Mines extends TankernnGame {
|
|
|
calculateMinesAround(tile, settings.pattern);
|
|
|
}
|
|
|
|
|
|
- public void calculateMinesAround(Tile tile, Pos[] pattern) {
|
|
|
+ public void calculateMinesAround(Tile tile, List<Pos> pattern) {
|
|
|
hiddenTiles--;
|
|
|
|
|
|
- int minesAround = 0;
|
|
|
- List<Tile> testTiles = new ArrayList<Tile>();
|
|
|
-
|
|
|
- for (int i = 0; i < pattern.length; i++) {
|
|
|
- try {
|
|
|
- testTiles.add(tiles[tile.pos.x + pattern[i].x][tile.pos.y + pattern[i].y]);
|
|
|
- } catch (ArrayIndexOutOfBoundsException e) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- }
|
|
|
+ List<Tile> testTiles = pattern.stream().map(p -> new Pos(p.x + tile.pos.x, p.y + tile.pos.y))
|
|
|
+ .map(p -> tiles.get(p)).filter(t -> t != null).collect(Collectors.toList());
|
|
|
|
|
|
- for (Tile testTile : testTiles)
|
|
|
- if (testTile.isMine)
|
|
|
- minesAround++;
|
|
|
+ int minesAround = (int) testTiles.stream().filter(t -> t.isMine).count();
|
|
|
|
|
|
tile.setMinesAround(minesAround);
|
|
|
-
|
|
|
+
|
|
|
// Keep checking if there are no mines around
|
|
|
if (minesAround == 0)
|
|
|
- for (Tile testTile : testTiles)
|
|
|
- if (testTile.getState().equals(Tile.TileState.HIDDEN))
|
|
|
- calculateMinesAround(testTile, pattern);
|
|
|
+ testTiles.stream().filter(t -> t.getState().equals(Tile.TileState.HIDDEN))
|
|
|
+ .forEach(t -> calculateMinesAround(t, pattern));
|
|
|
+
|
|
|
}
|
|
|
|
|
|
private void lose() {
|
|
|
running = false;
|
|
|
// Expose all mines
|
|
|
- for (Tile[] tArr : tiles)
|
|
|
- for (Tile t : tArr)
|
|
|
- if (t.isMine)
|
|
|
- t.setState(TileState.EXPLODED);
|
|
|
+ for (Tile t : tiles.values())
|
|
|
+ if (t.isMine)
|
|
|
+ t.setState(TileState.EXPLODED);
|
|
|
}
|
|
|
|
|
|
private void win() {
|
|
@@ -173,47 +153,30 @@ public class Mines extends TankernnGame {
|
|
|
|
|
|
@Override
|
|
|
public void update() {
|
|
|
- // { // Command selection
|
|
|
- // String[] command = sc.nextLine().split(" ");
|
|
|
- //
|
|
|
- // int x = Integer.parseInt(command[1]), y =
|
|
|
- // Integer.parseInt(command[2]);
|
|
|
- //
|
|
|
- // switch (command[0]) {
|
|
|
- // case "flag":
|
|
|
- // tiles[x][y].toggleFlag();
|
|
|
- // break;
|
|
|
- // case "check":
|
|
|
- // check(tiles[x][y]);
|
|
|
- // break;
|
|
|
- // default:
|
|
|
- // System.out.println("Unknown command.");
|
|
|
- // }
|
|
|
- // }
|
|
|
- { // Mouse selection
|
|
|
- if (Mouse.isButtonDown(1) || Mouse.isButtonDown(0)) {
|
|
|
- if (!justClicked && running) {
|
|
|
- justClicked = true;
|
|
|
- int x, y;
|
|
|
- x = Mouse.getX() / tileWidth;
|
|
|
- y = Mouse.getY() / tileHeight;
|
|
|
-
|
|
|
- if (Mouse.isButtonDown(0)) {
|
|
|
- check(tiles[x][y]);
|
|
|
- } else {
|
|
|
- tiles[x][y].toggleFlag();
|
|
|
- }
|
|
|
+ if (Mouse.isButtonDown(1) || Mouse.isButtonDown(0)) {
|
|
|
+ if (!justClicked && running) {
|
|
|
+ justClicked = true;
|
|
|
+ Pos p = new Pos(Mouse.getX() / tileWidth, Mouse.getY() / tileHeight);
|
|
|
+ if (!tiles.containsKey(p)) {
|
|
|
+ System.out.println(p);
|
|
|
+ dumpMap();
|
|
|
+ return;
|
|
|
}
|
|
|
- } else
|
|
|
- justClicked = false;
|
|
|
- }
|
|
|
-
|
|
|
+ if (Mouse.isButtonDown(0)) {
|
|
|
+ check(tiles.get(p));
|
|
|
+ } else {
|
|
|
+ tiles.get(p).toggleFlag();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ justClicked = false;
|
|
|
+
|
|
|
// Handle keyboard
|
|
|
if (running)
|
|
|
timeText.setText(format.format(((float) (Sys.getTime() - startTime)) / Sys.getTimerResolution()));
|
|
|
else if (Keyboard.isKeyDown(Keyboard.KEY_R))
|
|
|
startGame();
|
|
|
- else if (Keyboard.isKeyDown(Keyboard.KEY_E) && (editor == null || !editor.isShowing()))
|
|
|
+ if (Keyboard.isKeyDown(Keyboard.KEY_E) && (editor == null || !editor.isShowing()))
|
|
|
editor = new SettingsEditor(this);
|
|
|
|
|
|
if (hiddenTiles == settings.mines)
|
|
@@ -226,42 +189,33 @@ public class Mines extends TankernnGame {
|
|
|
public void render() {
|
|
|
List<GuiTexture> toRender = new ArrayList<GuiTexture>();
|
|
|
|
|
|
- for (int y = 0; y < settings.boardHeight; y++) {
|
|
|
- for (int x = 0; x < settings.boardWidth; x++) {
|
|
|
- Tile t = tiles[x][y];
|
|
|
- // Text output
|
|
|
- System.out.print(t.getState().equals(TileState.CHECKED) ? Integer.toString(t.getMinesAround())
|
|
|
- : t.getState().appearance);
|
|
|
- // OpenGL output
|
|
|
- Texture tex;
|
|
|
- switch (t.getState()) {
|
|
|
- case CHECKED:
|
|
|
- tex = checked[t.getMinesAround()];
|
|
|
- break;
|
|
|
- case EXPLODED:
|
|
|
- tex = exploded;
|
|
|
- break;
|
|
|
- case FLAGGED:
|
|
|
- tex = flagged;
|
|
|
- break;
|
|
|
- case HIDDEN:
|
|
|
- tex = hidden;
|
|
|
- break;
|
|
|
- default:
|
|
|
- tex = hidden;
|
|
|
- break;
|
|
|
- }
|
|
|
- Vector2f scale = new Vector2f(1f / settings.boardWidth, 1f / settings.boardHeight);
|
|
|
- toRender.add(new GuiTexture(tex,
|
|
|
- new Vector2f(2 * scale.x * t.pos.x + scale.x - 1, 2 * scale.y * t.pos.y + scale.y - 1), scale));
|
|
|
+ for (Tile t : tiles.values()) {
|
|
|
+ // OpenGL output
|
|
|
+ Texture tex;
|
|
|
+ switch (t.getState()) {
|
|
|
+ case CHECKED:
|
|
|
+ tex = checked[t.getMinesAround()];
|
|
|
+ break;
|
|
|
+ case EXPLODED:
|
|
|
+ tex = exploded;
|
|
|
+ break;
|
|
|
+ case FLAGGED:
|
|
|
+ tex = flagged;
|
|
|
+ break;
|
|
|
+ case HIDDEN:
|
|
|
+ default:
|
|
|
+ tex = hidden;
|
|
|
+ break;
|
|
|
}
|
|
|
- System.out.println();
|
|
|
+ Vector2f scale = new Vector2f(1f / settings.boardWidth, 1f / settings.boardHeight);
|
|
|
+ toRender.add(new GuiTexture(tex,
|
|
|
+ new Vector2f(2 * scale.x * t.pos.x + scale.x - 1, 2 * scale.y * t.pos.y + scale.y - 1), scale));
|
|
|
}
|
|
|
- System.out.println("-------------------");
|
|
|
|
|
|
renderer.render(toRender);
|
|
|
|
|
|
super.render();
|
|
|
+
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -269,6 +223,20 @@ public class Mines extends TankernnGame {
|
|
|
super.cleanUp();
|
|
|
}
|
|
|
|
|
|
+ public static Stream<Pos> allPositions(int width, int height) {
|
|
|
+ Stream<Integer> xs = IntStream.range(0, width).boxed();
|
|
|
+ return xs.flatMap(x -> IntStream.range(0, height).mapToObj(y -> new Pos(x, y)));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void dumpMap() {
|
|
|
+ // Text output
|
|
|
+ System.out.println("Size: " + tiles.size());
|
|
|
+ for (Pos p : tiles.keySet()) {
|
|
|
+ Tile t = tiles.get(p);
|
|
|
+ System.out.println("Pos: " + p + " Tile: " + t);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private void updateBoardSize() {
|
|
|
tileWidth = Display.getWidth() / settings.boardWidth;
|
|
|
tileHeight = Display.getHeight() / settings.boardHeight;
|