/
2048.py
132 lines (113 loc) · 3.66 KB
/
2048.py
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
"""
Modeled after https://github.com/fewf/curtsies_2048
TODO: Background colors, boldness for different numbers
"""
import random, time
import sturm
def main():
board = make_board()
with sturm.cbreak_mode():
while True:
heading = "Use the arrow keys, or Q to quit.\n\n"
game_over = all(not list(move(board)) for move in [up,down,left,right])
score = "You win!" if is_won(board) else "You lose!" if game_over else ""
sturm.render(heading + view(board) + "\n\n" + score + "\n")
if game_over: break
key = sturm.get_key()
if key.upper() == 'Q': break
elif key in globals(): # Hacky hacky, sorry. :P
sliding = list(globals()[key](board))
if sliding:
for board in sliding:
sturm.render(heading + view(board))
time.sleep(0.03)
board = plop(board, 2 if random.random() < .9 else 4)
# A board is a tuple of 4 rows;
# a row is a tuple of 4 values;
# a value is 0 for empty, or a positive number.
def make_board(): return plop(plop(empty_board, 2), 2)
empty_board = ((0,)*4,)*4
# Pre: board has at least one empty square.
def plop(board, v):
return update(board, random_empty(board), v)
def random_empty(board):
return random.choice([(r,c)
for r, row in enumerate(board)
for c, v in enumerate(row)
if v == 0])
def update(board, pos, new_v):
return tuple(tuple(new_v if (r,c) == pos else v
for c, v in enumerate(row))
for r, row in enumerate(board))
def view(board):
return '\n\n'.join(' '.join(('%d' % v if v else '.').center(4)
for v in row)
for row in board)
def is_won(board): return any(row.count(2048) for row in board)
def flipv(board): return board[::-1] # vertical flip
def flipd(board): return tuple(zip(*board)) # diagonal
def fliph(board): return flipd(flipv(flipd(board))) # horizontal
# Arrow-key actions:
def up(board): return map(flipd, left(flipd(board)))
def down(board): return map(flipd, right(flipd(board)))
def right(board): return map(fliph, left(fliph(board)))
def left(board):
states = tuple((0, row) for row in board)
while True:
states = tuple(collapsing(lo,row) for lo,row in states)
if all(lo is None for lo,_ in states):
break
yield tuple(row for _,row in states)
def collapsing(lo, row):
if lo is None:
return lo, row
for i in range(1, 4):
if row[i-1] == 0 and row[i] != 0:
break
if lo < i and row[i-1] and row[i-1] == row[i]:
lo = i
break
else:
return None, row
return lo, row[:i-1] + (row[i-1] + row[i],) + row[i+1:] + (0,)
# For testing
def collapse(row):
lo = 0
while True:
lo, row = collapsing(lo, row)
if lo is None: break
print row
## collapse((0, 0, 0, 0))
## collapse((2, 4, 2, 2))
#. (2, 4, 4, 0)
## collapse((2, 2, 2, 2))
#. (4, 2, 2, 0)
#. (4, 4, 0, 0)
## collapse((0, 2, 0, 2))
#. (2, 0, 2, 0)
#. (2, 2, 0, 0)
#. (4, 0, 0, 0)
## collapse((2, 0, 2, 0))
#. (2, 2, 0, 0)
#. (4, 0, 0, 0)
## collapse((2, 0, 0, 2))
#. (2, 0, 2, 0)
#. (2, 2, 0, 0)
#. (4, 0, 0, 0)
## collapse((0, 0, 0, 2))
#. (0, 0, 2, 0)
#. (0, 2, 0, 0)
#. (2, 0, 0, 0)
## collapse((0, 2, 4, 4))
#. (2, 4, 4, 0)
#. (2, 8, 0, 0)
## collapse((0, 2, 2, 4))
#. (2, 2, 4, 0)
#. (4, 4, 0, 0)
## collapse((2, 2, 2, 4))
#. (4, 2, 4, 0)
## collapse((2, 2, 4, 4))
#. (4, 4, 4, 0)
#. (4, 8, 0, 0)
if __name__ == '__main__':
main()