From 43d85b19782ba94feaf2414fc2d4cfbf4ef817fd Mon Sep 17 00:00:00 2001 From: Jacob Signorovitch Date: Sun, 20 Apr 2025 11:18:29 -0400 Subject: [PATCH] Almost done. Need to add endgame check. After grid is full (freeidx.size == 0), create copy of the grid and try move in each direction. If nothing changes, game over. --- .../src/twentyfortyeight/Main.java | 200 +++++++----------- 1 file changed, 79 insertions(+), 121 deletions(-) diff --git a/twentyfortyeight/src/twentyfortyeight/Main.java b/twentyfortyeight/src/twentyfortyeight/Main.java index 314cda3..a09aea1 100644 --- a/twentyfortyeight/src/twentyfortyeight/Main.java +++ b/twentyfortyeight/src/twentyfortyeight/Main.java @@ -10,17 +10,9 @@ import tester.Tester; class ImgUtil { static Color color(int n) { - Double hh = Math.log(n) / Math.log(2); - int hash = Integer.hashCode(hh.intValue() << 5); - int r, g, b; + int r = (int)Math.min(255, 20 * (int)(Math.log(n) / Math.log(2))); - // Shift the hash so the least significant byte is the part you want, - // and mask to isolate that byte. Also bias towards yellow a bit (heh). - r = ((hash >> 16) & 0xff) | 0xa0; // Boost red by setting the high bit. - g = ((hash >> 8) & 0xff) | 0x90; // Boost green by setting the high bit. - b = (hash & 0xff) & 0x7f; // Reduce blue by clearing the high bit. - - return new Color(r, g + 20, b); + return new Color(r, r / 2 + 80, r / 3 + 20); } // Create a tile image for a number. @@ -64,10 +56,12 @@ class Game extends World { } // Convenience constructor for init with custom settings. - Game(int w, int h, int seed) { + Game(int w, int h) { this.grid = new Grid(w, h); this.score = 0; - this.rand = new Random(seed); + this.rand = new Random(); + this.w = this.grid.w * SCALE; + this.h = this.grid.h * SCALE; this.scene = new WorldScene(this.grid.w, this.grid.h); } @@ -93,9 +87,7 @@ class Game extends World { List frees = this.grid.freeCellIdxs(); // Add tiles - this.grid.buf.set( - frees.get(0), 2 // this.rand.nextInt() % (frees.size() - 1)), 2 - ); + this.grid.buf.set(frees.get(this.rand.nextInt(frees.size())), 2); this.draw(); } @@ -136,6 +128,7 @@ class Grid { // Get the indexes of all "unfree" cells -- those whose value in buf is not // 0, and represent a used tile. + /* List unfreeCellIdxs() { List unfree = new ArrayList<>(); for (int i = 0; i < this.sz; i++) @@ -143,13 +136,17 @@ class Grid { return unfree; } + */ // Set the tile at the coords. Grid set(int x, int y, int v) { - this.buf.set(this.w * y + x, v); + this.buf.set(this.where(x, y), v); return this; } + // Get the index for the coord. + int where(int x, int y) { return this.w * y + x; } + // Get the value at the coords. int get(int x, int y) { return this.buf.get(this.w * y + x); } @@ -159,126 +156,88 @@ class Grid { // Get the y coord for an index. int iy(int i) { return i % this.w; } - // Given an index and a direction, is there a free there? - boolean freeDir(int i, int d) { - if (d == 0) { // Left. - if (i % this.w == 0) return false; // At an edge. - return this.buf.get(i - 1) == 0; - } else if (d == 1) { // Down. - if ((i + 1 + this.w) > this.sz) return false; // At an edge - return this.buf.get(i + this.w) == 0; - } else if (d == 2) { // Up. - if ((i - this.w) < 0) return false; - return this.buf.get(i - this.w) == 0; - } else if (d == 3) { // Right. - if ((i + 1) % this.w == 0) return false; // At an edge. - return this.buf.get(i + 1) == - 0; // Check if next space over is free. - } - - else - throw new IllegalArgumentException("Bad direction code given."); - } - - // Combine numbers that are the same into their sum. Start checking for - // combinations from the side the direction is to for each row/column. - void combine(int d) { - if (d == 0) { - for (int y = 0; y < this.h; y++) { - int prv = this.get(0, y); - for (int x = 1; x < this.w; x++) { - int cur = this.get(x, y); - if (cur == prv) { - this.set(x - 1, y, cur * 2); - this.set(x, y, 0); - } - } - } - } else if (d == 1) { - for (int x = 0; x < this.w; x++) { - int prv = this.get(x, this.h - 1); - for (int y = this.h - 2; y > 0; y--) { - int cur = this.get(x, y); - if (cur == prv) { - this.set(x, y + 1, cur * 2); - this.set(x, y, 0); - } - } - } - } else if (d == 2) { - for (int x = 0; x < this.w; x++) { - int prv = this.get(x, 0); - for (int y = 1; y < this.h; y++) { - int cur = this.get(x, y); - if (cur == prv) { - this.set(x, y - 1, cur * 2); - this.set(x, y, 0); - } - } - } - } else if (d == 3) { - for (int y = 0; y < this.h; y++) { - int prv = this.get(this.w - 1, y); - for (int x = this.w - 2; x > 0; x--) { - int cur = this.get(x, y); - if (cur == prv) { - this.set(x + 1, y, cur * 2); - this.set(x, y, 0); - } - } - } + void mv(int d) { + // For each row / col. + for (int i = 0; i < (d % 3 == 0 ? this.h : this.w); i++) { + while (this.lnMv(i, d)); + this.squish(i, d); + while (this.lnMv(i, d)); } } - // Get the indexes of all moveable tiles in a given direction -- those - // that are abutted by a free cell in the specified direction. - List mvblDir(int d) { - List mvbl = new ArrayList(); - - for (int i : this.unfreeCellIdxs()) - if (this.freeDir(i, d)) mvbl.add(i); - - return mvbl; + // Get the ith row. + List getRow(int i) { + return this.buf.subList( + this.where(0, i), this.where(this.w - 1, i) + 1 + ); } - // Move the tile at the index 1 space in the specified direction. - void mvTile(int i, int d) { - System.out.println("attempting to move"); - if (d == 0) this.buf.set(i - 1, this.buf.get(i)); - else if (d == 1) this.buf.set(i + this.w, this.buf.get(i)); - else if (d == 2) this.buf.set(i - this.w, this.buf.get(i)); - else if (d == 3) this.buf.set(i + 1, this.buf.get(i)); - else throw new IllegalArgumentException("Bad direction code given."); + // Get the ith col. A bit more complex. + List getCol(int i) { + if (i < 0 || i >= this.w) throw new IndexOutOfBoundsException("No."); - this.buf.set(i, 0); // Reset original. + return new ArrayList() { + public Integer get(int j) { + if (j < 0 || j >= h) throw new IndexOutOfBoundsException("No."); + return buf.get(w * j + i); + } + + public Integer set(int j, Integer val) { + if (j < 0 || j >= h) throw new IndexOutOfBoundsException("No."); + int prv = buf.get(w * j + i); + buf.set(w * j + i, val); + return prv; + } + + public int size() { return h; } + }; } - // Partial move -- apply movement rules once. There still may be tiles - // which can move after this is done. Return true if there are still - // more moves possible. - boolean partMv(int d) { - // 1. Find the tiles that can move. - List mvbl = this.mvblDir(d); - if (mvbl.isEmpty()) return false; - // 2. Move each tile to its new location. - for (int i : mvbl) { this.mvTile(i, d); } + // Get the line. + List getLn(int i, int d) { + return d == 0 ? this.getRow(i) + : d == 1 ? this.getCol(i).reversed() + : d == 2 ? this.getCol(i) + : this.getRow(i).reversed(); + } + + // Move the specified line in the specified direction once. If there is a + // combination, return false. If there are no more moves possible, return + // false. + boolean lnMv(int i, int d) { + List ln = getLn(i, d); + + int c = 0; // Moves made in line. + for (int j = 1; j < ln.size(); j++) { + if (ln.get(j - 1) == 0 && ln.get(j) != 0) { + ln.set(j - 1, ln.get(j)); + ln.set(j, 0); + } else c++; + } + + // If nothing has been done, give up. + if (c == ln.size() - 1) return false; + return true; } - // Move all tiles in that direction until they can't move or combine any - // more. - void mv(int d) { - while (this.partMv(d)); // I love side effects :). - this.combine(d); - // while (this.partMv(d)); // Complete move. + // Squish like tiles together. + void squish(int i, int d) { + List ln = getLn(i, d); + for (int j = 0; j < ln.size() - 1; j++) { + if (ln.get(j) != 0 && ln.get(j) == ln.get(j + 1)) { + ln.set(j, 2 * ln.get(j)); + ln.set(j + 1, 0); + } + } } // Render the grid. WorldScene render() { WorldScene bg = new WorldScene(Game.SCALE * this.w, Game.SCALE * this.h); - for (int i = 0; i < this.h; i++) - for (int j = 0; j < this.w; j++) { + for (int i = 0; i < this.w; i++) + for (int j = 0; j < this.h; j++) { int n = this.get(i, j); int coordx = i * Game.SCALE + Game.SCALE / 2; int coordy = j * Game.SCALE + Game.SCALE / 2; @@ -338,8 +297,7 @@ class Examples { } void testGame(Tester t) { - game = new Game(); - + game = new Game(2, 2); game.launchGame(); } }