-
Notifications
You must be signed in to change notification settings - Fork 56
/
Copy path2048game.py
241 lines (206 loc) · 8.17 KB
/
2048game.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
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# -*- coding: utf-8 -*-
"""2048game.ipynb
Automatically generated by Colaboratory.
Original file is located at
https://colab.research.google.com/drive/1iqx6ZtBWbQiL0yzeTpsufsUNrwzuwPfz
2048 is a sliding tile game to combine exponentially-increasing numbers.
More info at https://en.wikipedia.org/wiki/2048_(video_game)
"""
import random, sys
# Constant declaration.
BLANK = ""
def getNewBoard():
"""
Return a data structure that represent a board.
It's a dictionary with keys of (x,y) tuples and values of Tile at that space.
The tile is either a power-of-two interger or BLANK.
The coordinates are laid out as:
X 0 1 2 3
Y +-+-+-+-+
0 | | | | |
+-+-+-+-+
1 | | | | |
+-+-+-+-+
2 | | | | |
+-+-+-+-+
3 | | | | |
+-+-+-+-+
"""
newBoard = {} #Contains the board data structure to be returned.
# Loop over every possible space and set all the tiles to blank:
for x in range(4):
for y in range(4):
newBoard[(x,y)] = BLANK
# Pick two random spaces for the two starting 2's:
startingTwosPlaced =0
while startingTwosPlaced < 2:
randomSpace = (random.randint(0, 3), random.randint(0, 3))
#Check whether selected space isn't already taken:
if newBoard[randomSpace] == BLANK:
newBoard[randomSpace] = 2
startingTwosPlaced += 1
return newBoard
def drawBoard(board):
"""Draw the board data structure on the screen."""
# Go through each posiible space left to right, top to bottom, and
# create a list of what each space's label should be.
labels = [] # A list of strings for the number/blank for that tile.
for y in range(4):
for x in range(4):
title = board[(x,y)] #Get the tile at this space.
#make sure the label is 5 space long:
labelForThisTile = str(title).center(5)
labels.append(labelForThisTile)
# The {} are replaced with the label for that tile:
print(""" +-----+-----+-----+-----+
| | | | |
|{}|{}|{}|{}|
| | | | |
+-----+-----+-----+-----+
| | | | |
|{}|{}|{}|{}|
| | | | |
+-----+-----+-----+-----+
| | | | |
|{}|{}|{}|{}|
| | | | |
+-----+-----+-----+-----+
| | | | |
|{}|{}|{}|{}|
| | | | |
+-----+-----+-----+-----+
""".format(*labels))
def getScore(board):
"""Returns the sum of all the tiles on the board data structure."""
score = 0
# Loop over every space and add the tile to the score:
for x in range(4):
for y in range(4):
# Only add non-blank tiles to the score:
if board[(x,y)] != BLANK:
score = score + board[(x,y)]
return score
def combineTilesInColumn(column):
"""The column is a list of four tile. Index 0 is the "bottom" of
the column, and tiles are pulled "down" and combine if they are the
same. For example, combineTilesInColumn([2, BLANK, 2, BLANK])
returns [4, BLANK, BLANK, BLANK]."""
# Copy only the numbers (not blanks) from column to combinedTiles
combinedTiles = [] # A list of the non-blank tiles in column.
for i in range(4):
if column[i] != BLANK:
combinedTiles.append(column[i])
# Keep adding blanks until there are 4 tiles:
while len(combinedTiles) < 4:
combinedTiles.append(BLANK)
# Combine numbers if the one "above" it is the same, and double it.
for i in range(3): # Skip index 3: it's the topmost space.
if combinedTiles[i] == combinedTiles[i + 1]:
combinedTiles[i] *= 2 # Double the number in the tile.
# Move the tiles above it down one space:
for aboveIndex in range(i + 1, 3):
combinedTiles[aboveIndex] = combinedTiles[aboveIndex + 1]
combinedTiles[3] = BLANK # Topmost space is always BLANK.
return combinedTiles
def makeMove(board, move):
"""Carries out the move on the board.
The move argument is either 'W', 'A', 'S', or 'D' and the function
returns the resulting board data structure."""
# The board is split up into four columns, which are different
# depending on the direction of the move:
if move == 'W':
allColumnsSpaces = [[(0, 0), (0, 1), (0, 2), (0, 3)],
[(1, 0), (1, 1), (1, 2), (1, 3)],
[(2, 0), (2, 1), (2, 2), (2, 3)],
[(3, 0), (3, 1), (3, 2), (3, 3)]]
elif move == 'A':
allColumnsSpaces = [[(0, 0), (1, 0), (2, 0), (3, 0)],
[(0, 1), (1, 1), (2, 1), (3, 1)],
[(0, 2), (1, 2), (2, 2), (3, 2)],
[(0, 3), (1, 3), (2, 3), (3, 3)]]
elif move == 'S':
allColumnsSpaces = [[(0, 3), (0, 2), (0, 1), (0, 0)],
[(1, 3), (1, 2), (1, 1), (1, 0)],
[(2, 3), (2, 2), (2, 1), (2, 0)],
[(3, 3), (3, 2), (3, 1), (3, 0)]]
elif move == 'D':
allColumnsSpaces = [[(3, 0), (2, 0), (1, 0), (0, 0)],
[(3, 1), (2, 1), (1, 1), (0, 1)],
[(3, 2), (2, 2), (1, 2), (0, 2)],
[(3, 3), (2, 3), (1, 3), (0, 3)]]
# The board data structure after making the move:
boardAfterMove = {}
for columnSpaces in allColumnsSpaces: # Loop over all 4 columns.
# Get the tiles of this column (The first tile is the "bottom"
# of the column):
firstTileSpace = columnSpaces[0]
secondTileSpace = columnSpaces[1]
thirdTileSpace = columnSpaces[2]
fourthTileSpace = columnSpaces[3]
firstTile = board[firstTileSpace]
secondTile = board[secondTileSpace]
thirdTile = board[thirdTileSpace]
fourthTile = board[fourthTileSpace]
# Form the column and combine the tiles in it:
column = [firstTile, secondTile, thirdTile, fourthTile]
combinedTilesColumn = combineTilesInColumn(column)
# Set up the new board data structure with the combined tiles:
boardAfterMove[firstTileSpace] = combinedTilesColumn[0]
boardAfterMove[secondTileSpace] = combinedTilesColumn[1]
boardAfterMove[thirdTileSpace] = combinedTilesColumn[2]
boardAfterMove[fourthTileSpace] = combinedTilesColumn[3]
return boardAfterMove
def askForPlayerMove():
"""Asks the player for the direction of their next move (or quit).
Ensures they enter a valid move: either 'W', 'A', 'S' or 'D'."""
print('Enter move: (WASD or Q to quit)')
while True: # Keep looping until they enter a valid move.
move = input('> ').upper()
if move == 'Q':
# End the program:
print('Thanks for playing!')
sys.exit()
# Either return the valid move, or loop back and ask again:
if move in ('W', 'A', 'S', 'D'):
return move
else:
print('Enter one of "W", "A", "S", "D", or "Q".')
def addTwoToBoard(board):
"""Adds a new 2 tile randomly to the board."""
while True:
randomSpace = (random.randint(0, 3), random.randint(0, 3))
if board[randomSpace] == BLANK:
board[randomSpace] = 2
return # Return after finding one non-blank tile.
def isFull(board):
"""Returns True if the board data structure has no blanks."""
# Loop over every space on the board:
for x in range(4):
for y in range(4):
# If a space is blank, return False:
if board[(x, y)] == BLANK:
return False
return True # No space is blank, so return True.
def main():
print("""Slide all the tiles on the board in one of four directions. Tiles with
like numbers will combine into larger-numbered tiles. A new 2 tile is
added to the board on each move. You win if you can create a 2048 tile.
You lose if the board fills up the tiles before then.""")
input('Press Enter to begin...')
gameBoard = getNewBoard()
while True: # Main game loop.
drawBoard(gameBoard)
print('Score:', getScore(gameBoard))
playerMove = askForPlayerMove()
gameBoard = makeMove(gameBoard, playerMove)
addTwoToBoard(gameBoard)
if isFull(gameBoard):
drawBoard(gameBoard)
print('Game Over - Thanks for playing!')
sys.exit()
# If this program was run (instead of imported), run the game:s
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
sys.exit() # When Ctrl-C is pressed, end the program.