#D2048 The 2048 written in D.
##How to use
- Place file as follow:
d2048Root/
|-source/
| \- d2048/
| |- core.d
| \- game.d
\- app.d
- Execute
dub init
at d2048Root - Execute
dub
import std.stdio; | |
import std.string; | |
import d2048.game; | |
void main(){ | |
D2048Game d2048 = new D2048Game; | |
d2048.startGame; | |
} |
module d2048.core; | |
import std.stdio, | |
std.typecons, | |
std.algorithm.iteration, | |
std.random, | |
std.range; | |
class D2048Core{ | |
alias Position = Tuple!(int, "x", int, "y"); | |
static defaultWorldSize = Position(4, 4); | |
private int worldX, | |
worldY; | |
private int[][] world; | |
public int score; | |
this(){ | |
this(defaultWorldSize); | |
} | |
this(Position worldSize){ | |
worldX = worldSize.x; | |
worldY = worldSize.y; | |
world.length = worldY; | |
init; | |
} | |
void init(){ | |
foreach(ref row; world){ | |
row.length = worldX; | |
} | |
} | |
bool check(int[] array){ | |
int prev = -1; | |
if(array.filter!(e => e != 0).array == null){ | |
return false; | |
} | |
if(array[0] != 0){ | |
if(array[1..$].filter!(e => e != 0).array == null) | |
return false; | |
} | |
foreach(i, n; array){ | |
if(prev == 0) | |
return true; | |
else if(n == prev) | |
return true; | |
prev = n; | |
} | |
return false; | |
} | |
bool checkLeft(){ | |
foreach(row; world) | |
if(check(row)) | |
return true; | |
return false; | |
} | |
bool checkRight(){ | |
foreach(row; world) | |
if(check(arrayReverse(row))) | |
return true; | |
return false; | |
} | |
bool checkDown(){ | |
foreach(i; worldX.iota){ | |
int[] col = worldY.iota.map!(j => world[j][i]).array; | |
if(check(arrayReverse(col))) | |
return true; | |
} | |
return false; | |
} | |
bool checkUp(){ | |
foreach(i; worldX.iota){ | |
int[] col = worldY.iota.map!(j => world[j][i]).array; | |
if(check(col)) | |
return true; | |
} | |
return false; | |
} | |
int[] deleteZero(int[] row){ | |
int[] zeros; | |
zeros.length = row.filter!"a == 0".array.length; | |
row = row.filter!"a != 0".array ~ zeros; | |
return row; | |
} | |
int[] move(int[] row){ | |
int prev = -1; | |
row = deleteZero(row); | |
foreach(i, n; row){ | |
if(n == prev){ | |
row[i] *= 2; | |
row[i-1] = 0; | |
row = deleteZero(row); | |
prev = n * 2; | |
score += row[i];//Add Score | |
} else { | |
prev = n; | |
} | |
} | |
return row; | |
} | |
void moveLeft(){ | |
foreach(ref row; world) | |
row = move(row); | |
} | |
void moveRight(){ | |
foreach(ref row; world) | |
row = arrayReverse(row); | |
moveLeft; | |
foreach(ref row; world) | |
row = arrayReverse(row); | |
} | |
void moveUp(){ | |
int[][] cols = worldX.iota.map!(x => worldY.iota.map!(y => world[y][x]).array).array; | |
foreach(ref col; cols) | |
col = move(col); | |
int[][] ws = worldY.iota.map!(y => worldX.iota.map!(x => cols[x][y]).array).array; | |
world = ws; | |
} | |
void moveDown(){ | |
world = arrayReverse(world); | |
moveUp; | |
world = arrayReverse(world); | |
} | |
bool gameOver(){ | |
if(!checkLeft | |
&& !checkRight | |
&& !checkUp | |
&& !checkDown) | |
return true; | |
else | |
return false; | |
} | |
void printWorld(){ | |
foreach(row; world) | |
writeln(row); | |
} | |
void spawnNewBlock(){ | |
Position[] positions; | |
Mt19937 gen; | |
foreach(y; worldY.iota) | |
foreach(x; worldX.iota){ | |
if(world[y][x] == 0) | |
positions ~= Position(x,y); | |
} | |
gen.seed(unpredictableSeed); | |
ulong idx = gen.front % positions.length; | |
Position newPosition = positions[idx]; | |
gen.seed(unpredictableSeed); | |
ulong N = gen.front % 100 + 1; | |
int newPoint = 1 <= N && N < 75 ? 2 : 4; | |
world[newPosition.y][newPosition.x] = newPoint; | |
} | |
T[] arrayReverse(T)(T[] base){ | |
T[] rV; | |
foreach_reverse(e; base) | |
rV ~= e; | |
return rV; | |
} | |
} |
module d2048.game; | |
import std.stdio, | |
std.string; | |
import d2048.core; | |
class D2048Game : D2048Core{ | |
private ulong moves; | |
this(){ | |
score = 0; | |
spawnNewBlock; | |
spawnNewBlock; | |
} | |
void startGame(){ | |
writeln("===================="); | |
writeln("D 2048"); | |
writeln("[inputs]"); | |
writeln("l: left"); | |
writeln("r: right"); | |
writeln("u: up"); | |
writeln("d: down"); | |
writeln("===================="); | |
while(!gameOver){ | |
bool noTouch; | |
printWorld; | |
writeln("--------------------"); | |
writeln("moves:", moves); | |
writeln("score:", score); | |
write("[input]=> "); | |
string input = readln.chomp; | |
switch(input){ | |
case "l": | |
moveLeft; | |
break; | |
case "r": | |
moveRight; | |
break; | |
case "u": | |
moveUp; | |
break; | |
case "d": | |
moveDown; | |
break; | |
default: | |
noTouch = true; | |
break; | |
} | |
if(!noTouch){ | |
spawnNewBlock; | |
moves++; | |
} | |
} | |
writeln("GAME OVER"); | |
writeln("moves:", moves); | |
writeln("score:", score); | |
} | |
} |