-
Notifications
You must be signed in to change notification settings - Fork 3
/
Prolog 2048.pl
158 lines (132 loc) · 5.87 KB
/
Prolog 2048.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/* -------------------------------------------------------------
Entry point, just create a blank grid and enter a 'game loop'
-------------------------------------------------------------*/
play_2048 :-
welcome_msg,
length(Grid, 16), maplist(=(' '), Grid), % create a blank grid
play(Grid, yes), !. % don't have to cut here but it makes the exit cleaner
/* -----------------------------------------------
Messages that will be printed at various points
-----------------------------------------------*/
welcome_msg :-
format('~nWelcome to the Prolog version of 2048~n~n'),
format('To play using w,s,a,d keys for movement, q to quit~n~n').
contrats_msg :- format('Congratulations, you reached 2048!~n~n').
loser_msg :- format('Uh Oh, you could not quite make it to 2048...~n~n').
quit_msg :- format('Bye then!~n~n').
/* -------------------
End game conditions
-------------------*/
player_not_won_yet(Grid) :- maplist(dif(2048), Grid).
player_wins(Grid) :- member(2048, Grid).
player_loses(G) :- move(up, G, G), move(down, G, G), move(left, G, G), move(right, G, G).
/* ---------
Game loop
---------*/
% First check if the player has reached the win condition, if not find how many spaces are left
play(Grid, _) :-
player_wins(Grid),
draw_grid(Grid),
contrats_msg.
play(Grid, CreateNewNum) :-
player_not_won_yet(Grid),
include(=(' '), Grid, Spaces), length(Spaces, NSpaces), % the number of spaces in the grid
play(Grid, CreateNewNum, NSpaces).
% knowing the number of spaces, determine if there is a space, and if we need a new number, and generate.
play(Grid, no, _) :- play(Grid).
play(Grid, yes, 0) :- play(Grid).
play(Grid, yes, NSpaces) :-
dif(NSpaces, 0),
random_space(NSpaces, Grid, GridWithRandom),
play(GridWithRandom).
% with the new number on the grid we can tell if the player has lost the game yet
% if not, draw the grid and get the next action by the player
play(Grid) :-
player_loses(Grid),
draw_grid(Grid),
loser_msg.
play(Grid) :-
\+ player_loses(Grid),
draw_grid(Grid),
next_move_by_player(Move),
player_made_move(Grid, Move).
% determine the result of player move
player_made_move(_, quit).
player_made_move(Grid, Move) :-
dif(Move, quit),
move(Move, Grid, Grid), % The move creating the same grid indicates that no merge was done
play(Grid, no). % don't create a new number
player_made_move(Grid, Move) :-
dif(Move, quit),
move(Move, Grid, MovedGrid),
dif(Grid, MovedGrid),
play(MovedGrid, yes).
/* ---------------------------------------
Get the next move from the player input
---------------------------------------*/
next_move_by_player(Move) :-
repeat,
get_single_char(Char),
key_move(Char, Move).
% valid keys are: up = 'w', down = 's', left = 'a' right = 'd', quit = 'q'
key_move(119, up). key_move(115, down). key_move(97, left). key_move(100, right). key_move(113, quit).
/* ------------------
Draw the Game grid
------------------*/
draw_grid([A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4,D1,D2,D3,D4]) :-
format( '+-------------------+~n'),
row([A1,A2,A3,A4]),
row([B1,B2,B3,B4]),
row([C1,C2,C3,C4]),
maplist(draw, [D1,D2,D3,D4]),
format('¦~n+-------------------+~n~n~n').
row([A,B,C,D]) :- maplist(draw, [A,B,C,D]), format('¦~n¦----+----+----+----¦~n').
draw(' ') :- format('¦ ').
draw(X) :- member(X,[2,4,8]), format('¦ ~d ', X).
draw(X) :- member(X,[16,32,64]), format('¦ ~d ', X).
draw(X) :- member(X,[128,256,512]), format('¦ ~d', X).
draw(X) :- member(X,[1024,2048]), format('¦~d', X).
/* ----------------------------------------
Populate a random space with a new value
----------------------------------------*/
random_space(0, G, G).
random_space(1, Grid, GridWithRandom) :-
four_or_two(V),
select(' ', Grid, V, GridWithRandom).
random_space(N, Grid, GridWithRandom) :-
N > 1,
four_or_two(V),
random(1, N, P),
replace_space(P, V, Grid, GridWithRandom).
replace_space(0, V, [' '|T], [V|T]).
replace_space(P, V, [' '|T], [' '|R]) :- succ(NextP, P), replace_space(NextP, V, T, R).
replace_space(P, V, [H|T], [H|R]) :- dif(' ', H), replace_space(P, V, T, R).
four_or_two(V) :- random(1, 10, IsFour), IsFour = 1 -> V = 4 ; V = 2.
/* ------------------------------------------
Process a game move based on the direction
------------------------------------------*/
move(Direction, UnMoved, Moved) :-
map_move(Direction, UnMoved, UnMovedMapped),
maplist(combine_row, UnMovedMapped, MovedMapped),
map_move(Direction, Moved, MovedMapped).
% convert the array to a set of lists that can be moved in the same
% direction. This can be reversed after the move is completed.
map_move(up, [A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4,D1,D2,D3,D4], [[D1,C1,B1,A1],[D2,C2,B2,A2],[D3,C3,B3,A3],[D4,C4,B4,A4]]).
map_move(down, [A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4,D1,D2,D3,D4], [[A1,B1,C1,D1],[A2,B2,C2,D2],[A3,B3,C3,D3],[A4,B4,C4,D4]]).
map_move(left, [A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4,D1,D2,D3,D4], [[A4,A3,A2,A1],[B4,B3,B2,B1],[C4,C3,C2,C1],[D4,D3,D2,D1]]).
map_move(right, [A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4,D1,D2,D3,D4], [[A1,A2,A3,A4],[B1,B2,B3,B4],[C1,C2,C3,C4],[D1,D2,D3,D4]]).
% remove all the spaces, then put them at the front of the list
combine_row(UnMoved, Moved) :-
partition(=(' '), UnMoved, Blank, Set),
append(Blank, Set, ReadyToMove),
combine(ReadyToMove, Moved).
% combine based on the rules of the game.
combine([A,B,C,D], [A,B,C,D]) :- dif(A,B), dif(B,C), dif(C,D).
combine([A,A,B,B], [' ',' ',Ad,Bd]) :- dbl(A,Ad), dbl(B,Bd).
combine([A,B,C,C], [' ',A,B,Cd]) :- dif(A,B), dbl(C,Cd).
combine([A,B,B,C], [' ',A,Bd,C]) :- dif(B,C), dbl(B,Bd).
combine([A,A,B,C], [' ',Ad,B,C]) :- dif(A,B), dif(B,C), dbl(A, Ad).
combine([A,B,C,C], [' ',A,B,Cd]) :- dif(A,B), dif(B,C), dbl(C,Cd).
% this could be done using maths, but it is more prology this way.
dbl(' ', ' ').
dbl(2,4). dbl(4,8). dbl(8,16). dbl(16,32). dbl(32,64). dbl(64,128). dbl(128,256). dbl(256,512). dbl(512,1028). dbl(1028,2048).