Mines.java 7.0 KB

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