From dfef396d17c739b88102d9c55b136308614908a8 Mon Sep 17 00:00:00 2001 From: Jacob Date: Mon, 12 May 2025 23:02:41 -0400 Subject: [PATCH 1/3] Fucking hell here we go again. --- twentyfortyeight/Main2.java | 303 ++++++++++++++++ .../src/twentyfortyeight/Main.java | 340 ++++-------------- 2 files changed, 367 insertions(+), 276 deletions(-) create mode 100644 twentyfortyeight/Main2.java diff --git a/twentyfortyeight/Main2.java b/twentyfortyeight/Main2.java new file mode 100644 index 0000000..a09aea1 --- /dev/null +++ b/twentyfortyeight/Main2.java @@ -0,0 +1,303 @@ +package twentyfortyeight; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import javalib.impworld.*; +import javalib.worldimages.*; +import tester.Tester; + +class ImgUtil { + static Color color(int n) { + int r = (int)Math.min(255, 20 * (int)(Math.log(n) / Math.log(2))); + + return new Color(r, r / 2 + 80, r / 3 + 20); + } + + // Create a tile image for a number. + static WorldImage mktile(int n) { + return new OverlayImage( + new TextImage( + String.valueOf(n), Game.SCALE / 2, FontStyle.BOLD, Color.BLACK + ), + new RectangleImage( + Game.SCALE, Game.SCALE, OutlineMode.SOLID, ImgUtil.color(n) + ) + ); + } + + // Create a fee space image. + static WorldImage mkfree() { + return new RectangleImage( + Game.SCALE, Game.SCALE, OutlineMode.OUTLINE, Color.GRAY + ); + } +} + +// A game of 2048. +class Game extends World { + static int SCALE = 100; + + int score; + Grid grid; + Random rand; + WorldScene scene; + int w, h; // The pixel widths and heights. + + // Convenience constructor for init with default settings. + Game() { + this.grid = new Grid(4, 4); + this.score = 0; + this.rand = new Random(); + this.w = this.grid.w * SCALE; + this.h = this.grid.h * SCALE; + this.scene = new WorldScene(this.w, this.h); + } + + // Convenience constructor for init with custom settings. + Game(int w, int h) { + this.grid = new Grid(w, h); + this.score = 0; + 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); + } + + void launchGame() { + this.bigBang(this.w, this.h, 0.01); + this.grid.set(0, 0, 2).set(0, 2, 2); + } + + public WorldScene makeScene() { + this.scene = this.getEmptyScene(); + this.draw(); + return this.scene; + } + + public void onKeyEvent(String key) { + if (key.equals("left") || key.equals("h")) this.grid.mv(0); + else if (key.equals("down") || key.equals("j")) this.grid.mv(1); + else if (key.equals("up") || key.equals("k")) this.grid.mv(2); + else if (key.equals("right") || key.equals("l")) this.grid.mv(3); + else return; // Don't draw or add tiles if bad key pressed. + + this.draw(); + + List frees = this.grid.freeCellIdxs(); + // Add tiles + this.grid.buf.set(frees.get(this.rand.nextInt(frees.size())), 2); + + this.draw(); + } + + // Draw current game state. + void draw() { this.scene = this.grid.render(); } +} + +// The grid in which the game is played. +class Grid { + int w; // The width of the grid. + int h; // The height of the grid. + ArrayList buf; // The buffer containing the tiles. + int sz; // The size of the buffer -- always w * h. + + Grid(int w, int h) { + if (w < 1 || h < 1) + throw new IllegalArgumentException("Can't make grid that small."); + + this.w = w; + this.h = h; + this.sz = this.w * this.h; + this.buf = new ArrayList<>(this.sz); + + // Fill with zeros. + for (int i = 0; i < this.sz; this.buf.add(i++ * 0)); + } + + // Get the indexes of all "free" cells -- those whose value in buf is 0, + // and are represented as blank tile. + List freeCellIdxs() { + List free = new ArrayList<>(); + for (int i = 0; i < this.sz; i++) + if (this.buf.get(i) == 0 && free.add(i)) continue; + + return free; + } + + // 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++) + if (this.buf.get(i) != 0 && unfree.add(i)) continue; + + return unfree; + } + */ + + // Set the tile at the coords. + Grid set(int x, int y, int 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); } + + // Get the x coord for an index. + int ix(int i) { return i % this.h; } + + // Get the y coord for an index. + int iy(int i) { return i % this.w; } + + 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 ith row. + List getRow(int i) { + return this.buf.subList( + this.where(0, i), this.where(this.w - 1, i) + 1 + ); + } + + // Get the ith col. A bit more complex. + List getCol(int i) { + if (i < 0 || i >= this.w) throw new IndexOutOfBoundsException("No."); + + 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; } + }; + } + + // 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; + } + + // 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.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; + if (n == 0) bg.placeImageXY(ImgUtil.mkfree(), coordx, coordy); + else bg.placeImageXY(ImgUtil.mktile(n), coordx, coordy); + } + + return bg; + } +} + +class Examples { + Grid verySmall, small, oblong, regular; + + Game game; + + void init() { + verySmall = new Grid(1, 1); + small = new Grid(2, 2); + oblong = new Grid(1, 3); + regular = new Grid(4, 4); + } + + void testGridFreeCellIdxs(Tester t) { + init(); + t.checkExpect( + small.freeCellIdxs(), new ArrayList(List.of(0, 1, 2, 3)) + ); + t.checkExpect( + verySmall.freeCellIdxs(), new ArrayList(List.of(0)) + ); + t.checkExpect( + oblong.freeCellIdxs(), new ArrayList(List.of(0, 1, 2)) + ); + } + + void testGridSet(Tester t) { + init(); + small.set(0, 0, 1); + t.checkExpect(small.freeCellIdxs(), new ArrayList<>(List.of(1, 2, 3))); + small.set(1, 1, 1); + t.checkExpect(small.freeCellIdxs(), new ArrayList<>(List.of(1, 2))); + } + + void testRegular(Tester t) { + init(); + regular.set(1, 0, 16) + .set(2, 0, 16) + .set(3, 0, 32) + .set(0, 1, 4) + .set(1, 1, 4) + .set(4, 1, 2); + + t.checkExpect(regular.get(1, 0), 16); + t.checkExpect(regular.get(0, 0), 0); + t.checkExpect(regular.get(3, 3), 0); + } + + void testGame(Tester t) { + game = new Game(2, 2); + game.launchGame(); + } +} diff --git a/twentyfortyeight/src/twentyfortyeight/Main.java b/twentyfortyeight/src/twentyfortyeight/Main.java index a09aea1..5cf97c1 100644 --- a/twentyfortyeight/src/twentyfortyeight/Main.java +++ b/twentyfortyeight/src/twentyfortyeight/Main.java @@ -2,302 +2,90 @@ package twentyfortyeight; import java.awt.Color; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Random; import javalib.impworld.*; import javalib.worldimages.*; import tester.Tester; -class ImgUtil { - static Color color(int n) { - int r = (int)Math.min(255, 20 * (int)(Math.log(n) / Math.log(2))); +// Config constants. +class Util { + // Game scale. + static int scale = 24; +} - return new Color(r, r / 2 + 80, r / 3 + 20); +// The board on which the game is played. +class Board { + int sz; // The side length of the board grid square. + Map board; + + Board(int sz) { + this.sz = sz; + this.board = new HashMap(); } - // Create a tile image for a number. - static WorldImage mktile(int n) { - return new OverlayImage( - new TextImage( - String.valueOf(n), Game.SCALE / 2, FontStyle.BOLD, Color.BLACK - ), - new RectangleImage( - Game.SCALE, Game.SCALE, OutlineMode.SOLID, ImgUtil.color(n) - ) + // Draw the board. + WorldImage draw() { + WorldImage img; + ArrayList rows = new ArrayList<>(); + + // For each row. + for (int y = 0; y < this.sz; y++) { + ArrayList row = new ArrayList<>(); + // For each element in the row. + for (int x = 0; x < this.sz; x++) + row.add(this.board.get(new Coord(x, y)).draw()); + + // Add row to the list. + rows.add(row.stream().reduce( + new EmptyImage(), + (cell1, cell2) -> new BesideImage(cell1, cell2) + )); + } + + // Collapse rows into single image. + return rows.stream().reduce( + new EmptyImage(), + (row1, row2) -> new BesideAlignImage(AlignModeY.BOTTOM, row1, row2) ); } - // Create a fee space image. - static WorldImage mkfree() { - return new RectangleImage( - Game.SCALE, Game.SCALE, OutlineMode.OUTLINE, Color.GRAY - ); - } -} + // A moveable tile on the board. + class Tile { + int n; -// A game of 2048. -class Game extends World { - static int SCALE = 100; + Tile(int n) { this.n = n; } - int score; - Grid grid; - Random rand; - WorldScene scene; - int w, h; // The pixel widths and heights. + // Generate the color of the tile. + Color col() { + int r = + (int)Math.min(255, 20 * (int)(Math.log(this.n) / Math.log(2))); + return new Color(r, r / 2 + 80, r / 3 + 20); + } - // Convenience constructor for init with default settings. - Game() { - this.grid = new Grid(4, 4); - this.score = 0; - this.rand = new Random(); - this.w = this.grid.w * SCALE; - this.h = this.grid.h * SCALE; - this.scene = new WorldScene(this.w, this.h); - } - - // Convenience constructor for init with custom settings. - Game(int w, int h) { - this.grid = new Grid(w, h); - this.score = 0; - 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); - } - - void launchGame() { - this.bigBang(this.w, this.h, 0.01); - this.grid.set(0, 0, 2).set(0, 2, 2); - } - - public WorldScene makeScene() { - this.scene = this.getEmptyScene(); - this.draw(); - return this.scene; - } - - public void onKeyEvent(String key) { - if (key.equals("left") || key.equals("h")) this.grid.mv(0); - else if (key.equals("down") || key.equals("j")) this.grid.mv(1); - else if (key.equals("up") || key.equals("k")) this.grid.mv(2); - else if (key.equals("right") || key.equals("l")) this.grid.mv(3); - else return; // Don't draw or add tiles if bad key pressed. - - this.draw(); - - List frees = this.grid.freeCellIdxs(); - // Add tiles - this.grid.buf.set(frees.get(this.rand.nextInt(frees.size())), 2); - - this.draw(); - } - - // Draw current game state. - void draw() { this.scene = this.grid.render(); } -} - -// The grid in which the game is played. -class Grid { - int w; // The width of the grid. - int h; // The height of the grid. - ArrayList buf; // The buffer containing the tiles. - int sz; // The size of the buffer -- always w * h. - - Grid(int w, int h) { - if (w < 1 || h < 1) - throw new IllegalArgumentException("Can't make grid that small."); - - this.w = w; - this.h = h; - this.sz = this.w * this.h; - this.buf = new ArrayList<>(this.sz); - - // Fill with zeros. - for (int i = 0; i < this.sz; this.buf.add(i++ * 0)); - } - - // Get the indexes of all "free" cells -- those whose value in buf is 0, - // and are represented as blank tile. - List freeCellIdxs() { - List free = new ArrayList<>(); - for (int i = 0; i < this.sz; i++) - if (this.buf.get(i) == 0 && free.add(i)) continue; - - return free; - } - - // 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++) - if (this.buf.get(i) != 0 && unfree.add(i)) continue; - - return unfree; - } - */ - - // Set the tile at the coords. - Grid set(int x, int y, int 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); } - - // Get the x coord for an index. - int ix(int i) { return i % this.h; } - - // Get the y coord for an index. - int iy(int i) { return i % this.w; } - - 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)); + // Draw the tile. + WorldImage draw() { + return new RectangleImage( + Util.scale, Util.scale, OutlineMode.SOLID, this.col() + ); } } - // Get the ith row. - List getRow(int i) { - return this.buf.subList( - this.where(0, i), this.where(this.w - 1, i) + 1 - ); - } + // Board coordinates. + class Coord { + int x, y; - // Get the ith col. A bit more complex. - List getCol(int i) { - if (i < 0 || i >= this.w) throw new IndexOutOfBoundsException("No."); - - 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; } - }; - } - - // 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++; + Coord(int x, int y) { + this.x = x; + this.y = y; } - // If nothing has been done, give up. - if (c == ln.size() - 1) return false; - - return true; - } - - // 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); - } + public boolean equals(Object that) { + if (!(that instanceof Coord)) return false; + else return ((Coord)that).x == this.x && ((Coord)that).y == this.y; } + + public int hashCode() { return 31 * Integer.hashCode(this.x + this.y); } } - - // Render the grid. - WorldScene render() { - WorldScene bg = - new WorldScene(Game.SCALE * this.w, Game.SCALE * this.h); - 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; - if (n == 0) bg.placeImageXY(ImgUtil.mkfree(), coordx, coordy); - else bg.placeImageXY(ImgUtil.mktile(n), coordx, coordy); - } - - return bg; - } -} - -class Examples { - Grid verySmall, small, oblong, regular; - - Game game; - - void init() { - verySmall = new Grid(1, 1); - small = new Grid(2, 2); - oblong = new Grid(1, 3); - regular = new Grid(4, 4); - } - - void testGridFreeCellIdxs(Tester t) { - init(); - t.checkExpect( - small.freeCellIdxs(), new ArrayList(List.of(0, 1, 2, 3)) - ); - t.checkExpect( - verySmall.freeCellIdxs(), new ArrayList(List.of(0)) - ); - t.checkExpect( - oblong.freeCellIdxs(), new ArrayList(List.of(0, 1, 2)) - ); - } - - void testGridSet(Tester t) { - init(); - small.set(0, 0, 1); - t.checkExpect(small.freeCellIdxs(), new ArrayList<>(List.of(1, 2, 3))); - small.set(1, 1, 1); - t.checkExpect(small.freeCellIdxs(), new ArrayList<>(List.of(1, 2))); - } - - void testRegular(Tester t) { - init(); - regular.set(1, 0, 16) - .set(2, 0, 16) - .set(3, 0, 32) - .set(0, 1, 4) - .set(1, 1, 4) - .set(4, 1, 2); - - t.checkExpect(regular.get(1, 0), 16); - t.checkExpect(regular.get(0, 0), 0); - t.checkExpect(regular.get(3, 3), 0); - } - - void testGame(Tester t) { - game = new Game(2, 2); - game.launchGame(); - } -} From 3edaf673c8296413e6a3d055c0d8b79f9d37bf7e Mon Sep 17 00:00:00 2001 From: Jacob Date: Mon, 12 May 2025 23:40:58 -0400 Subject: [PATCH 2/3] why. --- .../src/twentyfortyeight/Main.java | 148 +++++++++++++----- 1 file changed, 110 insertions(+), 38 deletions(-) diff --git a/twentyfortyeight/src/twentyfortyeight/Main.java b/twentyfortyeight/src/twentyfortyeight/Main.java index 5cf97c1..d1c4b48 100644 --- a/twentyfortyeight/src/twentyfortyeight/Main.java +++ b/twentyfortyeight/src/twentyfortyeight/Main.java @@ -10,25 +10,67 @@ import javalib.impworld.*; import javalib.worldimages.*; import tester.Tester; +class Examples { + void testRun(Tester t) { + Game game = new Game(); + game.board.board.put(new Coord(0, 0), new Tile(2)); + game.board.board.put(new Coord(0, 1), new Tile(20)); + game.run(); + } +} + // Config constants. class Util { // Game scale. - static int scale = 24; + static int scale = 70; + + // Default 2048 game board width. + static int defaultWidth = 4; +} + +// A game of 2048. +class Game extends World { + int score; // The current score. + int sz; // The size of the grid. + int width; // The width of the image in pixels. + Board board; // The game board. + + Game(int score, int sz, int width, Board board) { + this.score = score; + this.sz = sz; + this.width = this.sz * Util.scale; + this.board = board; + } + + // Convenince constructor with default parameters. + Game() { + this.score = 0; + this.sz = Util.defaultWidth; + this.width = this.sz * Util.scale; + this.board = new Board(this.sz); + } + + public WorldScene makeScene() { + WorldScene scene = new WorldScene(this.width, this.width); + scene.placeImageXY(this.board.draw(), this.width / 2, this.width / 2); + return scene; + } + + void run() { this.bigBang(this.width, this.width); } } // The board on which the game is played. class Board { int sz; // The side length of the board grid square. - Map board; + Map board; Board(int sz) { this.sz = sz; - this.board = new HashMap(); + this.board = new HashMap(); } // Draw the board. WorldImage draw() { - WorldImage img; ArrayList rows = new ArrayList<>(); // For each row. @@ -36,7 +78,7 @@ class Board { ArrayList row = new ArrayList<>(); // For each element in the row. for (int x = 0; x < this.sz; x++) - row.add(this.board.get(new Coord(x, y)).draw()); + row.add(this.get(new Coord(x, y)).draw()); // Add row to the list. rows.add(row.stream().reduce( @@ -47,45 +89,75 @@ class Board { // Collapse rows into single image. return rows.stream().reduce( - new EmptyImage(), - (row1, row2) -> new BesideAlignImage(AlignModeY.BOTTOM, row1, row2) + new EmptyImage(), (row1, row2) -> new AboveImage(row1, row2) ); } - // A moveable tile on the board. - class Tile { - int n; + Cell get(Coord coord) { + Cell gotten = this.board.get(coord); - Tile(int n) { this.n = n; } + // Check if there's something there, otherwise return empty space. + if (gotten == null) return new Space(); + else return gotten; + } +} - // Generate the color of the tile. - Color col() { - int r = - (int)Math.min(255, 20 * (int)(Math.log(this.n) / Math.log(2))); - return new Color(r, r / 2 + 80, r / 3 + 20); - } +// A cell on the board. +abstract class Cell { + // Generate the color of the tile. + Color col() { return Color.PINK; } - // Draw the tile. - WorldImage draw() { - return new RectangleImage( - Util.scale, Util.scale, OutlineMode.SOLID, this.col() - ); - } + // Draw the cell. + WorldImage draw() { + return new RectangleImage( + Util.scale, Util.scale, OutlineMode.SOLID, this.col() + ); + } +} + +// A moveable tile on the board. +class Tile extends Cell { + int n; + + Tile(int n) { this.n = n; } + + Color col() { + // int r = (int)Math.min(255, 20 * (int)(Math.log(this.n) / + // Math.log(2))); return new Color(r, r / 2 + 80, r / 3 + 20); + return Color.RED; } - // Board coordinates. - class Coord { - int x, y; - - Coord(int x, int y) { - this.x = x; - this.y = y; - } - - public boolean equals(Object that) { - if (!(that instanceof Coord)) return false; - else return ((Coord)that).x == this.x && ((Coord)that).y == this.y; - } - - public int hashCode() { return 31 * Integer.hashCode(this.x + this.y); } + WorldImage draw() { + return new OverlayImage( + new TextImage( + String.valueOf(this.n), + (Util.scale / Math.ceil((Math.log(this.n) / Math.log(10)))) * + 0.7, + Color.BLACK + ), + super.draw() + ); } +} + +// An empty space on the board. +class Space extends Cell { + Color col() { return Color.BLUE; } +} + +// Board coordinates. +class Coord { + int x, y; + + Coord(int x, int y) { + this.x = x; + this.y = y; + } + + public boolean equals(Object that) { + if (!(that instanceof Coord)) return false; + else return ((Coord)that).x == this.x && ((Coord)that).y == this.y; + } + + public int hashCode() { return 31 * Integer.hashCode(this.x + this.y); } +} From 8319e00baeca62a00f1f98684b10d62770fb64c1 Mon Sep 17 00:00:00 2001 From: Jacob Date: Tue, 13 May 2025 00:22:18 -0400 Subject: [PATCH 3/3] too late. --- .../src/twentyfortyeight/Main.java | 59 ++++++++++++++++--- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/twentyfortyeight/src/twentyfortyeight/Main.java b/twentyfortyeight/src/twentyfortyeight/Main.java index d1c4b48..7a50004 100644 --- a/twentyfortyeight/src/twentyfortyeight/Main.java +++ b/twentyfortyeight/src/twentyfortyeight/Main.java @@ -15,6 +15,8 @@ class Examples { Game game = new Game(); game.board.board.put(new Coord(0, 0), new Tile(2)); game.board.board.put(new Coord(0, 1), new Tile(20)); + game.board.board.put(new Coord(0, 2), new Tile(200)); + game.board.board.put(new Coord(0, 3), new Tile(2000)); game.run(); } } @@ -22,10 +24,25 @@ class Examples { // Config constants. class Util { // Game scale. - static int scale = 70; + static int scale = 256; // Default 2048 game board width. static int defaultWidth = 4; + + // Calculate an appropriate font size for a number. + static double fontSize(int n) { + // Start scaling numbers down once they pass 2 digits. + double x = 0.73 * Math.pow(Math.ceil(Math.log(n) / Math.log(100)), -1); + return Util.scale * x; + } + + // Convert a movement to a displacement vector. + Coord moveDisp(Move move) { + if (move.equals(Move.LEFT)) return new Coord(-1, 0); + else if (move.equals(Move.DOWN)) return new Coord(0, -1); + else if (move.equals(Move.UP)) return new Coord(0, 1); + else return new Coord(0, 1); + } } // A game of 2048. @@ -56,9 +73,22 @@ class Game extends World { return scene; } + public void onKeyEvent(String key) { + if (key.equals("left") || key.equals("h")) this.board.move(Move.LEFT); + else if (key.equals("down") || key.equals("j")) + this.board.move(Move.DOWN); + else if (key.equals("up") || key.equals("k")) this.board.move(Move.UP); + else if (key.equals("right") || key.equals("l")) + this.board.move(Move.RIGHT); + else return; + } + void run() { this.bigBang(this.width, this.width); } } +// The possible movements. +enum Move { UP, LEFT, RIGHT, DOWN } + // The board on which the game is played. class Board { int sz; // The side length of the board grid square. @@ -93,6 +123,7 @@ class Board { ); } + // Get the cell at the coords. Cell get(Coord coord) { Cell gotten = this.board.get(coord); @@ -100,6 +131,16 @@ class Board { if (gotten == null) return new Space(); else return gotten; } + + // Move in the given direction. + void move(Move move) { + for (int x = 0; x < this.sz; x++) { + for (int y = 0; y < this.sz; y++) { + // Copy logic from old code here. + Cell cell = this.get(new Coord(x, y)); + } + } + } } // A cell on the board. @@ -122,18 +163,14 @@ class Tile extends Cell { Tile(int n) { this.n = n; } Color col() { - // int r = (int)Math.min(255, 20 * (int)(Math.log(this.n) / - // Math.log(2))); return new Color(r, r / 2 + 80, r / 3 + 20); - return Color.RED; + int r = (int)Math.min(255, 20 * (int)(Math.log(this.n) / Math.log(2))); + return new Color(r, r / 2 + 80, r / 3 + 20); } WorldImage draw() { return new OverlayImage( new TextImage( - String.valueOf(this.n), - (Util.scale / Math.ceil((Math.log(this.n) / Math.log(10)))) * - 0.7, - Color.BLACK + String.valueOf(this.n), Util.fontSize(this.n), Color.BLACK ), super.draw() ); @@ -142,7 +179,7 @@ class Tile extends Cell { // An empty space on the board. class Space extends Cell { - Color col() { return Color.BLUE; } + Color col() { return Color.LIGHT_GRAY; } } // Board coordinates. @@ -154,6 +191,10 @@ class Coord { this.y = y; } + Coord add(Coord that) { return that.addHelper(this.x, this.y); } + + Coord addHelper(int x, int y) { return new Coord(this.x + x, this.y + y); } + public boolean equals(Object that) { if (!(that instanceof Coord)) return false; else return ((Coord)that).x == this.x && ((Coord)that).y == this.y;