Created
July 2, 2023 08:09
-
-
Save u1i/09735edf24a368ce8515131aac02c8bf to your computer and use it in GitHub Desktop.
2048 Game in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import curses | |
from random import randrange, choice | |
from collections import defaultdict | |
# Initialize the game | |
actions = ['Up', 'Down', 'Left', 'Right'] | |
keys = [ord('w'), ord('s'), ord('a'), ord('d')] | |
actions_dict = dict(zip(keys, actions)) | |
def transpose(field): | |
return [list(row) for row in zip(*field)] | |
def invert(field): | |
return [row[::-1] for row in field] | |
class Game2048: | |
def __init__(self, size=4, win_value=2048): | |
self.size = size | |
self.win_value = win_value | |
self.reset() | |
def reset(self): | |
self.score = 0 | |
self.board = [[0] * self.size for _ in range(self.size)] | |
self.spawn() | |
self.spawn() | |
def spawn(self): | |
new_element = 4 if randrange(100) > 89 else 2 | |
(i, j) = choice([(i, j) for i in range(self.size) for j in range(self.size) if self.board[i][j] == 0]) | |
self.board[i][j] = new_element | |
def move(self, direction): | |
def move_row_left(row): | |
def tighten(row): | |
new_row = [i for i in row if i != 0] | |
new_row += [0] * (len(row) - len(new_row)) | |
return new_row | |
def merge(row): | |
pair = False | |
new_row = [] | |
for i in range(len(row)): | |
if pair: | |
new_row.append(2 * row[i]) | |
self.score += 2 * row[i] | |
pair = False | |
else: | |
if i + 1 < len(row) and row[i] == row[i + 1]: | |
pair = True | |
new_row.append(0) | |
else: | |
new_row.append(row[i]) | |
return new_row | |
return tighten(merge(tighten(row))) | |
moves = {} | |
moves['Left'] = lambda field: [move_row_left(row) for row in field] | |
moves['Right'] = lambda field: invert(moves['Left'](invert(field))) | |
moves['Up'] = lambda field: transpose(moves['Left'](transpose(field))) | |
moves['Down'] = lambda field: transpose(moves['Right'](transpose(field))) | |
if direction in moves: | |
if self.move_possible(direction): | |
self.board = moves[direction](self.board) | |
self.spawn() | |
return True | |
else: | |
return False | |
def move_possible(self, direction): | |
def row_left_possible(row): | |
def change(i): | |
if row[i] == 0 and row[i + 1] != 0: | |
return True | |
if row[i] != 0 and row[i] == row[i + 1]: | |
return True | |
return False | |
return any(change(i) for i in range(len(row) - 1)) | |
checks = {} | |
checks['Left'] = lambda field: any(row_left_possible(row) for row in field) | |
checks['Right'] = lambda field: checks['Left'](invert(field)) | |
checks['Up'] = lambda field: checks['Left'](transpose(field)) | |
checks['Down'] = lambda field: checks['Right'](transpose(field)) | |
if direction in checks: | |
return checks[direction](self.board) | |
else: | |
return False | |
def is_game_over(self): | |
return all(self.move_possible(action) is False for action in actions) | |
def is_win(self): | |
return any(any(tile >= self.win_value for tile in row) for row in self.board) | |
def draw(self, screen): | |
help_string1 = '(W)Up (S)Down (A)Left (D)Right' | |
help_string2 = ' R)Restart (Q)Exit' | |
win_string = ' You Win!' | |
def cast(string): | |
screen.addstr(string + '\n') | |
def draw_separator(): | |
cast('+------' * self.size + '+') | |
def draw_row(row): | |
cast(''.join('|{: ^6} '.format(num) if num > 0 else '| ' for num in row) + '|') | |
screen.clear() | |
cast('SCORE: ' + str(self.score)) | |
if self.is_win(): | |
screen.addstr(win_string) | |
else: | |
if self.is_game_over(): | |
cast(' Game Over!') | |
else: | |
cast('') | |
draw_separator() | |
for row in self.board: | |
draw_row(row) | |
draw_separator() | |
cast(help_string1) | |
cast(help_string2) | |
def play(self, screen): | |
def get_user_action(screen): | |
char = 'N' | |
while char not in actions_dict: | |
char = screen.getch() | |
return actions_dict[char] | |
self.reset() | |
self.draw(screen) | |
while True: | |
action = get_user_action(screen) | |
if action == 'Restart': | |
self.reset() | |
elif action == 'Exit': | |
break | |
elif self.move(action): | |
if self.is_win(): | |
self.draw(screen) | |
break | |
self.draw(screen) | |
def main(stdscr): | |
game = Game2048() | |
game.play(stdscr) | |
curses.wrapper(main) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment