Mines.java 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. package eu.tankernn.mines;
  2. import java.io.FileNotFoundException;
  3. import java.text.DecimalFormat;
  4. import java.util.ArrayList;
  5. import java.util.Arrays;
  6. import java.util.Collections;
  7. import java.util.List;
  8. import java.util.Map;
  9. import java.util.stream.Collectors;
  10. import java.util.stream.IntStream;
  11. import java.util.stream.Stream;
  12. import org.lwjgl.Sys;
  13. import org.lwjgl.input.Keyboard;
  14. import org.lwjgl.input.Mouse;
  15. import org.lwjgl.opengl.Display;
  16. import org.lwjgl.util.vector.Vector2f;
  17. import eu.tankernn.gameEngine.GameLauncher;
  18. import eu.tankernn.gameEngine.TankernnGame;
  19. import eu.tankernn.gameEngine.loader.font.FontType;
  20. import eu.tankernn.gameEngine.loader.font.GUIText;
  21. import eu.tankernn.gameEngine.loader.textures.Texture;
  22. import eu.tankernn.gameEngine.renderEngine.gui.GuiRenderer;
  23. import eu.tankernn.gameEngine.renderEngine.gui.GuiTexture;
  24. import eu.tankernn.gameEngine.util.InternalFile;
  25. import eu.tankernn.mines.Tile.TileState;
  26. public class Mines extends TankernnGame {
  27. public static final String GAME_NAME = "Minesweeper";
  28. public static final Pos[] DEFAULT_PATTERN = { new Pos(0, 1), new Pos(0, -1), new Pos(1, 0), new Pos(1, 1),
  29. new Pos(1, -1), new Pos(-1, 0), new Pos(-1, -1), new Pos(-1, 1) };
  30. public static Settings DEFAULT_SETTINGS = new Settings(Arrays.asList(DEFAULT_PATTERN), 9, 9, 10);
  31. // Utility
  32. private DecimalFormat format = new DecimalFormat("0.000 sec");
  33. private GuiRenderer renderer;
  34. private SettingsEditor editor;
  35. // Graphics
  36. private Texture hidden, exploded, flagged;
  37. private Texture[] checked;
  38. private GUIText timeText;
  39. private int tileWidth, tileHeight;
  40. /**
  41. * Next game settings.
  42. */
  43. private Settings nextSettings;
  44. /**
  45. * Current game settings.
  46. */
  47. private Settings settings;
  48. // Game state
  49. private long startTime;
  50. private Map<Pos, Tile> tiles;
  51. private int hiddenTiles;
  52. private boolean running = false;
  53. private boolean justClicked = false;
  54. public Mines(String name) {
  55. super(name);
  56. renderer = new GuiRenderer(loader);
  57. try {
  58. hidden = loader.loadTexture("hidden.png");
  59. checked = new Texture[10];
  60. for (int i = 0; i < checked.length; i++)
  61. checked[i] = loader.loadTexture(i + ".png");
  62. exploded = loader.loadTexture("exploded.png");
  63. flagged = loader.loadTexture("flagged.png");
  64. FontType font = new FontType(loader.loadTexture("arial.png"), new InternalFile("arial.fnt"));
  65. timeText = new GUIText(format.format(0), 1f, font, new Vector2f(0f, 0f), 100, false);
  66. GUIText helpText = new GUIText("R - reset, E - edit settings", 1f, font, new Vector2f(0.8f, 0f), 0.15f,
  67. false);
  68. textMaster.loadText(timeText);
  69. textMaster.loadText(helpText);
  70. } catch (FileNotFoundException e) {
  71. e.printStackTrace();
  72. }
  73. setSettings(DEFAULT_SETTINGS);
  74. startGame();
  75. }
  76. public void setSettings(Settings settings) {
  77. this.nextSettings = settings;
  78. }
  79. /**
  80. * Starts game with next settings.
  81. */
  82. private void startGame() {
  83. if (!nextSettings.equals(settings)) {
  84. this.settings = nextSettings;
  85. updateBoardSize();
  86. }
  87. startTime = Sys.getTime();
  88. hiddenTiles = settings.area;
  89. tiles = generateBoard(settings.boardWidth, settings.boardHeight, settings.mines);
  90. running = true;
  91. }
  92. private Map<Pos, Tile> generateBoard(int width, int height, int mines) {
  93. assert width * height < mines;
  94. List<Pos> minePositions = new ArrayList<Pos>(allPositions(width, height).collect(Collectors.toList()));
  95. Collections.shuffle(minePositions);
  96. minePositions.subList(0, minePositions.size() - mines).clear();
  97. return allPositions(width, height).map(p -> new Tile(minePositions.contains(p), p))
  98. .collect(Collectors.toMap(t -> t.pos, t -> t));
  99. }
  100. public void check(Tile tile) {
  101. if (running)
  102. if (tile.isMine)
  103. lose();
  104. else
  105. calculateMinesAround(tile, settings.pattern);
  106. }
  107. public void calculateMinesAround(Tile tile, List<Pos> pattern) {
  108. hiddenTiles--;
  109. List<Tile> testTiles = pattern.stream().map(p -> new Pos(p.x + tile.pos.x, p.y + tile.pos.y))
  110. .map(p -> tiles.get(p)).filter(t -> t != null).collect(Collectors.toList());
  111. int minesAround = (int) testTiles.stream().filter(t -> t.isMine).count();
  112. tile.setMinesAround(minesAround);
  113. // Keep checking if there are no mines around
  114. if (minesAround == 0)
  115. testTiles.stream().filter(t -> t.getState().equals(Tile.TileState.HIDDEN))
  116. .forEach(t -> calculateMinesAround(t, pattern));
  117. }
  118. private void lose() {
  119. running = false;
  120. // Expose all mines
  121. for (Tile t : tiles.values())
  122. if (t.isMine)
  123. t.setState(TileState.EXPLODED);
  124. }
  125. private void win() {
  126. running = false;
  127. }
  128. @Override
  129. public void update() {
  130. if (Mouse.isButtonDown(1) || Mouse.isButtonDown(0)) {
  131. if (!justClicked && running) {
  132. justClicked = true;
  133. Pos p = new Pos(Mouse.getX() / tileWidth, Mouse.getY() / tileHeight);
  134. if (!tiles.containsKey(p)) {
  135. System.out.println(p);
  136. dumpMap();
  137. return;
  138. }
  139. if (Mouse.isButtonDown(0)) {
  140. check(tiles.get(p));
  141. } else {
  142. tiles.get(p).toggleFlag();
  143. }
  144. }
  145. } else
  146. justClicked = false;
  147. // Handle keyboard
  148. if (running)
  149. timeText.setText(format.format(((float) (Sys.getTime() - startTime)) / Sys.getTimerResolution()));
  150. else if (Keyboard.isKeyDown(Keyboard.KEY_R))
  151. startGame();
  152. if (Keyboard.isKeyDown(Keyboard.KEY_E) && (editor == null || !editor.isShowing()))
  153. editor = new SettingsEditor(this);
  154. if (hiddenTiles == settings.mines)
  155. win();
  156. super.update();
  157. }
  158. @Override
  159. public void render() {
  160. List<GuiTexture> toRender = new ArrayList<GuiTexture>();
  161. for (Tile t : tiles.values()) {
  162. // OpenGL output
  163. Texture tex;
  164. switch (t.getState()) {
  165. case CHECKED:
  166. tex = checked[t.getMinesAround()];
  167. break;
  168. case EXPLODED:
  169. tex = exploded;
  170. break;
  171. case FLAGGED:
  172. tex = flagged;
  173. break;
  174. case HIDDEN:
  175. default:
  176. tex = hidden;
  177. break;
  178. }
  179. Vector2f scale = new Vector2f(1f / settings.boardWidth, 1f / settings.boardHeight);
  180. toRender.add(new GuiTexture(tex,
  181. new Vector2f(2 * scale.x * t.pos.x + scale.x - 1, 2 * scale.y * t.pos.y + scale.y - 1), scale));
  182. }
  183. renderer.render(toRender);
  184. super.render();
  185. }
  186. @Override
  187. public void cleanUp() {
  188. super.cleanUp();
  189. }
  190. public static Stream<Pos> allPositions(int width, int height) {
  191. Stream<Integer> xs = IntStream.range(0, width).boxed();
  192. return xs.flatMap(x -> IntStream.range(0, height).mapToObj(y -> new Pos(x, y)));
  193. }
  194. private void dumpMap() {
  195. // Text output
  196. System.out.println("Size: " + tiles.size());
  197. for (Pos p : tiles.keySet()) {
  198. Tile t = tiles.get(p);
  199. System.out.println("Pos: " + p + " Tile: " + t);
  200. }
  201. }
  202. private void updateBoardSize() {
  203. tileWidth = Display.getWidth() / settings.boardWidth;
  204. tileHeight = Display.getHeight() / settings.boardHeight;
  205. }
  206. public static void main(String[] args) {
  207. GameLauncher.init(GAME_NAME, 800, 800);
  208. GameLauncher.launch(new Mines(GAME_NAME));
  209. }
  210. }