From f3118ddf96774a115026b75d3f2cb69f8671c01e Mon Sep 17 00:00:00 2001 From: helori_ollivier Date: Fri, 13 Mar 2026 14:46:08 +0100 Subject: [PATCH] added main game mechanics --- notes.md | 14 +- src/main/java/bzh/risotto/GameMap.java | 113 ++++++++++++++-- src/main/java/bzh/risotto/Minesweeper.java | 32 ++++- src/main/java/bzh/risotto/MinesweeperMap.java | 121 ++++++++++++++++++ src/main/java/bzh/risotto/Timer.java | 43 +++++++ src/main/java/bzh/risotto/Utils.java | 28 ++++ .../java/bzh/risotto/tilemap/TileMap.java | 6 + src/main/resources/ui.png | Bin 632 -> 631 bytes 8 files changed, 339 insertions(+), 18 deletions(-) create mode 100644 src/main/java/bzh/risotto/MinesweeperMap.java create mode 100644 src/main/java/bzh/risotto/Timer.java create mode 100644 src/main/java/bzh/risotto/Utils.java diff --git a/notes.md b/notes.md index 77986ad..77c67df 100644 --- a/notes.md +++ b/notes.md @@ -1,4 +1,10 @@ -down: 2 -up: 8704 -right: 273 -left: 1220 \ No newline at end of file + 0, 0, 0, 1, 1, 2,-1, 2, 1, 0 + 0, 0, 0, 1,-1, 2, 2,-1, 1, 0 + 0, 0, 0, 1, 1, 1, 1, 1, 1, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 1, 1, 1, 0, 0, 0, 0, 1, 1 + 0, 1,-1, 1, 0, 0, 0, 0, 1,-1 + 0, 1, 1, 1, 1, 2, 2, 1, 1, 1 + 0, 0, 1, 1, 2,-1,-1, 2, 1, 1 + 0, 0, 1,-1, 2, 2, 2, 2,-1, 2 + 0, 0, 1, 1, 1, 0, 0, 1, 2,-1 \ No newline at end of file diff --git a/src/main/java/bzh/risotto/GameMap.java b/src/main/java/bzh/risotto/GameMap.java index 4ddd75d..d4ad51d 100644 --- a/src/main/java/bzh/risotto/GameMap.java +++ b/src/main/java/bzh/risotto/GameMap.java @@ -3,41 +3,68 @@ package bzh.risotto; import bzh.risotto.tilemap.Tile; import bzh.risotto.tilemap.TileMap; import bzh.risotto.tilemap.TileSet; + import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Vector2; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.ArrayList; import java.util.List; +import static java.util.Collections.emptyMap; + +/** + * class that load, store and update the visual map of the game, + * according to the game actions + */ public class GameMap { - private final TileSet tileSet; + /** tilemap used by the gamemap */ private final TileMap tileMap; + /** overlay tilemap */ + private final TileMap overlayMap; + + /** list of tile that are not digged */ private List dirtTiles; private final Logger logger = LogManager.getLogger(); - public GameMap() { + /** + * Constructor of the gamemap + * + * @param size size of the map + */ + public GameMap(Vector2 size) { - this.tileSet = new TileSet("tileset.png", new Vector2(16,16), new Vector2(6,10)); - List> map = loadMap(); - this.tileMap = new TileMap(this.tileSet, map); + TileSet tileSet = new TileSet("tileset.png", new Vector2(16,16), new Vector2(6,10)); + + List> map = loadMap(size); + this.tileMap = new TileMap(tileSet, map); this.tileMap.setTile(5,0,0); + tileSet = new TileSet("ui.png", new Vector2(16,16), new Vector2(5,3)); + this.overlayMap = new TileMap(tileSet, Utils.emptyMap(size)); + loadConstArrays(); } - private List> loadMap() { + /** + * generate the gameMap + * + * @param size size of the map + * @return the generated map + */ + private List> loadMap(Vector2 size) { List> res = new ArrayList<>(); List row; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < size.y; i++) { row = new ArrayList<>(); - for (int j = 0; j < 10; j++) { + for (int j = 0; j < size.x; j++) { int tileId = (i+j)%3; @@ -49,19 +76,41 @@ public class GameMap { return res; } - public void dig(Vector2 worldCoord) { + /** + * Dig the gamemap at the coord + * convert grass tiles to dirt tiles, and make them visualy connect + * + * @param worldCoord the world coordinat to dig + */ + public void dig(Vector2 worldCoord, int nbMines) { + + // convert world coord to tilemap coord Vector2 coord = tileMap.toTileMapCoord(worldCoord); logger.debug("pos: " + coord); + logger.debug("tileType: " + getTileType((int) coord.x, (int) coord.y)); + // prevent diging if spot is marked + int overlayTileId = this.overlayMap.getTileId((int) coord.x, (int) coord.y); + if (overlayTileId == 1) { + return; + } + + // dig the tile tileMap.setTile(13, (int) coord.x, (int) coord.y); + + // update the surrounding tiles for (int i = -1; i < 2; i ++) { for (int j = -1; j < 2; j ++) { Tile tile = null; + + // prevent out of bound error while checking border tiles try { tile = tileMap.getTile((int) coord.x + j, (int) coord.y + i); } catch (IndexOutOfBoundsException e) { logger.info("Out of world check", e); } + + // set the right tile to make them visually connect if ( tile != null && !dirtTiles.contains(tile.getId())) { int tileType = getTileType((int) coord.x + j, (int) coord.y + i); int tileId = tileTypeToId(tileType); @@ -70,12 +119,40 @@ public class GameMap { } } } + + this.overlayMap.setTile((nbMines+5), (int) coord.x, (int) coord.y); } + public void mark(Vector2 worldCoord) { + Vector2 coord = tileMap.toTileMapCoord(worldCoord); + + int tileId = this.overlayMap.getTileId((int) coord.x, (int) coord.y); + + if (tileId == 0) { + this.overlayMap.setTile((1), (int) coord.x, (int) coord.y); + } + else if (tileId == 1) { + this.overlayMap.setTile((0), (int) coord.x, (int) coord.y); + } + } + + /** + * draw the map to the screen + * + * @param spriteBatch the spritebatch to use + */ public void render(SpriteBatch spriteBatch) { this.tileMap.render(spriteBatch); + this.overlayMap.render(spriteBatch); } + /** + * calculate the type of the tile, by checking the surrounding tiles + * + * @param coordX x coordinate of the tile to check + * @param coordY y coordinate of the tile to check + * @return the tile type + */ private int getTileType(int coordX, int coordY) { // count tiles around int p = 0; @@ -83,12 +160,15 @@ public class GameMap { for (int i = -1; i < 2; i ++) { for (int j = -1; j < 2; j ++) { Tile tile = null; + + // prevent out of bound error when checking border tiles try { tile = tileMap.getTile(coordX + j, coordY + i); } catch (IndexOutOfBoundsException e) { logger.info("Out of world check", e); } + // compute tile type if (tile != null && !dirtTiles.contains(tile.getId())) { tileType += (int) Math.pow(2, p); } @@ -102,10 +182,17 @@ public class GameMap { return tileType; } + /** + * convert tile type to tile set coordinate + * (could be generalized too fit every tileset) + *coordinate + * @param tileType the tile type we want to convert + * @return the corresponding coordinate in the tileset + */ private int tileTypeToId(int tileType) { return switch (tileType) { case 2, 3, 6, 7, 71, 323, 327, 66, 322, 67, 258, 259, 263, 262, 326, 70 -> 10; // down - case 128, 192, 448, 384, 385, 449, 453, 193, 132, 196, 452, 133, 197, 388, 389 -> 22; // up + case 128, 192, 448, 384, 385, 449, 453, 193, 132, 196, 452, 133, 197, 388, 389, 129 -> 22; // up case 8, 73, 72, 9, 329, 333, 328, 332, 77, 264, 268, 269, 12, 13, 76 -> 17; // right case 32, 288, 292, 36, 356, 357, 33, 97, 353, 96, 100, 101, 37, 293, 352, 289 -> 15; // left case 160, 161, 165, 229, 224, 228, 164, 225 -> 21; // inner bot-left corner @@ -121,7 +208,7 @@ public class GameMap { case 38, 102, 358, 359, 294, 295, 39, 103 -> 6; // top-left corner case 204, 205, 461, 456, 457, 200, 201, 460 -> 20; // bot-left corner case 416, 417, 481, 485, 420, 484, 421, 480 -> 18; // bot-right corner - case 11, 75, 79, 335, 267, 271, 351, 15 -> 8; // top-right corner + case 11, 75, 79, 335, 267, 271, 351, 15, 331 -> 8; // top-right corner case 47, 111, 367, 303 -> 7; // up-straight case 488, 496, 493, 492, 489 -> 19; // bot-straight case 203, 459, 463, 207 -> 14; // left-straight @@ -154,6 +241,10 @@ public class GameMap { }; } + public TileMap getTileMap() { + return this.tileMap; + } + private void loadConstArrays() { this.dirtTiles = new ArrayList<>(); this.dirtTiles.add(0); diff --git a/src/main/java/bzh/risotto/Minesweeper.java b/src/main/java/bzh/risotto/Minesweeper.java index dd7259f..7360e85 100644 --- a/src/main/java/bzh/risotto/Minesweeper.java +++ b/src/main/java/bzh/risotto/Minesweeper.java @@ -23,9 +23,13 @@ public class Minesweeper implements ApplicationListener { private Vector2 zoom; private GameMap gameMap; + private MinesweeperMap minesweeperMap; + private Timer clickDelay; private Logger logger = LogManager.getLogger(); + + @Override public void create() { @@ -37,7 +41,11 @@ public class Minesweeper implements ApplicationListener { zoom = new Vector2(Gdx.graphics.getHeight() / height, Gdx.graphics.getWidth() / width); - gameMap = new GameMap(); + gameMap = new GameMap(new Vector2(10, 10)); + minesweeperMap = new MinesweeperMap(new Vector2(10, 10), 10); + + clickDelay = new Timer(0.25); + } @Override @@ -54,9 +62,27 @@ public class Minesweeper implements ApplicationListener { private void logic() { - if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) { + if (Gdx.input.isButtonPressed(Input.Buttons.LEFT) && clickDelay.isFinished()) { Vector2 mousePos = getMouseWorldPos(); - gameMap.dig(mousePos); + int cell = minesweeperMap.getCell(this.gameMap.getTileMap().toTileMapCoord(mousePos)); + + gameMap.dig(mousePos, cell); + + if (cell == -1) { + logger.info("Perdu"); + } + else { + logger.info(cell); + } + + clickDelay.start(); + } + + if (Gdx.input.isButtonPressed(Input.Buttons.RIGHT) && clickDelay.isFinished()) { + Vector2 mousePos = getMouseWorldPos(); + gameMap.mark(mousePos); + + clickDelay.start(); } } diff --git a/src/main/java/bzh/risotto/MinesweeperMap.java b/src/main/java/bzh/risotto/MinesweeperMap.java new file mode 100644 index 0000000..8f718d0 --- /dev/null +++ b/src/main/java/bzh/risotto/MinesweeperMap.java @@ -0,0 +1,121 @@ +package bzh.risotto; + + +import bzh.risotto.tilemap.Tile; +import com.badlogic.gdx.math.Vector2; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + + +/** + * Class that load, store and update the mines map + */ +public class MinesweeperMap { + + private Vector2 size; + private List> map; + + private final Random rand = new Random(); + + private final Logger logger = LogManager.getLogger(); + + public MinesweeperMap(Vector2 size, int nbMines) { + this.size = size; + this.map = loadMap(size, nbMines); + } + + public int getCell(Vector2 coord) { + return this.map.get((int) coord.y).get((int) coord.x); + } + + private List> loadMap(Vector2 size, int nbMines) { + + // generate empty map + List> res = emptyMap(size); + + // set random mines + loadMines(res, nbMines); + + // count surrounding mines + countMines(res); + + logger.debug(res); + + return res; + } + + private List> emptyMap(Vector2 size) { + + List> res = new ArrayList<>(); + List row; + for (int i = 0; i < size.y; i++) { + row = new ArrayList<>(); + for (int j = 0; j < size.x; j++) { + row.add(0); + } + res.add(row); + } + + return res; + } + + private void loadMines(List> map, int nbMines) { + + while (nbMines > 0) { + + int coordX = this.rand.nextInt(map.getFirst().size()); + int coordY = this.rand.nextInt(map.size()); + + if (map.get(coordY).get(coordX) != -1) { + List row = map.get(coordY); + row.set(coordX, -1); + map.set(coordY, row); + + nbMines --; + } + } + } + + private void countMines(List> map) { + + for (int i = 0; i < map.size(); i++) { + for (int j = 0; j < map.getFirst().size(); j++) { + + if (map.get(i).get(j) != -1) { + int nbMines = countSurroundingMines(map, j, i); + List row = map.get(i); + row.set(j, nbMines); + map.set(i, row); + } + } + } + } + + private int countSurroundingMines(List> map, int coordX, int coordY) { + + int res = 0; + + for (int i = -1; i < 2; i ++) { + for (int j = -1; j < 2; j++) { + Integer cell = null; + + // prevent out of bound error while checking border tiles + try { + cell = map.get(coordY + i).get(coordX + j); + } catch (IndexOutOfBoundsException e) { + logger.info("Out of world check", e); + } + + if (cell != null && cell == -1) { + res ++; + } + } + } + + return res; + } +} diff --git a/src/main/java/bzh/risotto/Timer.java b/src/main/java/bzh/risotto/Timer.java new file mode 100644 index 0000000..5dd5816 --- /dev/null +++ b/src/main/java/bzh/risotto/Timer.java @@ -0,0 +1,43 @@ +package bzh.risotto; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class Timer { + + private double duration; + private double startTime; + + Logger logger = LogManager.getLogger(); + + public Timer(double duration) { + this.duration = duration; + } + + public void start() { + this.startTime = System.currentTimeMillis(); + } + + public void stop() { + this.startTime = -1; + } + + public boolean isFinished() { + return timeLeft() <= 0; + } + + public double timeLeft() { + + if (this.startTime > 0) { + double res = this.startTime - System.currentTimeMillis() + (duration * 1000); + if (res > 0) { + return res; + } + else { + return 0; + } + } + + return -1; + } +} diff --git a/src/main/java/bzh/risotto/Utils.java b/src/main/java/bzh/risotto/Utils.java new file mode 100644 index 0000000..2ab7020 --- /dev/null +++ b/src/main/java/bzh/risotto/Utils.java @@ -0,0 +1,28 @@ +package bzh.risotto; + +import com.badlogic.gdx.math.Vector2; + +import java.util.ArrayList; +import java.util.List; + +public class Utils { + + public static List> emptyMap(Vector2 size) { + + List> res = new ArrayList<>(); + + List row; + for (int i = 0; i < size.y; i++) { + row = new ArrayList<>(); + for (int j = 0; j < size.x; j++) { + + int tileId = 0; + + row.add(tileId); + } + res.add(row); + } + + return res; + } +} diff --git a/src/main/java/bzh/risotto/tilemap/TileMap.java b/src/main/java/bzh/risotto/tilemap/TileMap.java index ab32789..6b5e63b 100644 --- a/src/main/java/bzh/risotto/tilemap/TileMap.java +++ b/src/main/java/bzh/risotto/tilemap/TileMap.java @@ -114,6 +114,12 @@ public class TileMap { } } + /** + * convert world coord to tile map coord + * + * @param worldCoord world coordinate to convert + * @return tilemap coordinate + */ public Vector2 toTileMapCoord(Vector2 worldCoord) { Vector2 tileMapCoord = new Vector2(); diff --git a/src/main/resources/ui.png b/src/main/resources/ui.png index 06210a0b37b4846437077c8614ac1a3603a283c7..8a7584ab7efe695e6f01e4f84f9a4b6fe10e7848 100644 GIT binary patch delta 357 zcmV-r0h<2!1os56907kuNklRy$p6@U_NbfKNBX{Ulk`?r)Jc3&DAYDpPoR$)T;XQJ9 z=ub(oa4DI=3`^m@6ft;CYxvMYfEC+rHaNFYi;OFQ;KL1^kOn-JZK3V z$IM{L5;cK%FRl05n!NwJWz3UhpT>(&49K=)d`527~Q!$9xj7=0;!uy=^&4~XVZh~^iF zW*hw9Q3_NpnrB`Y2bYC|QgU!zBT($(AU*`*Lm)mSLB7L1c(%jxpi;*mzZ5CsOL?-x zW03CJCr7lw?pT{-tcqGrcbm%@U${g+!6Q0FGf;iPIF)Q~&?~07*qoM6N<$ Ef@UJC9RL6T