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.
This commit is contained in:
@@ -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<Integer> 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<Integer> unfreeCellIdxs() {
|
||||
List<Integer> 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<Integer> mvblDir(int d) {
|
||||
List<Integer> mvbl = new ArrayList<Integer>();
|
||||
|
||||
for (int i : this.unfreeCellIdxs())
|
||||
if (this.freeDir(i, d)) mvbl.add(i);
|
||||
|
||||
return mvbl;
|
||||
// Get the ith row.
|
||||
List<Integer> 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<Integer> getCol(int i) {
|
||||
if (i < 0 || i >= this.w) throw new IndexOutOfBoundsException("No.");
|
||||
|
||||
this.buf.set(i, 0); // Reset original.
|
||||
return new ArrayList<Integer>() {
|
||||
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<Integer> 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<Integer> 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<Integer> 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<Integer> 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();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user