Last active
November 29, 2018 10:47
-
-
Save greut/9cd6cac99f8053332c2518cc8f529ebe to your computer and use it in GitHub Desktop.
2048 en Python
This file contains hidden or 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
#!/usr/bin/env python3 | |
# vim: set ts=4 sts=0 et sw=4 smarttab : | |
"""2048 game in Python (cli, PyQt 4 or 5).""" | |
import time | |
import math | |
import sys | |
from random import choice | |
ROW = 4 | |
def show(board): | |
"""Print the 2048 board.""" | |
print("") | |
for i in range(0, len(board), ROW): | |
print("".join(("{0:^5}".format(cell) for cell in board[i:i+ROW]))) | |
print("") | |
def round(board): | |
""" | |
Insert a 2 at a random available position (where a 0 sits). | |
Return a new board. | |
""" | |
index = choice(tuple(all_indices(board, 0))) | |
return tuple(board[:index]) + (2,) + tuple(board[index+1:]) | |
def shake(board, direction, max=2048): | |
""" | |
Shake the board in a given direction in wasd. | |
Return a new board. | |
""" | |
rows = [*board] | |
direction = direction.lower() | |
if direction not in 'wasd': | |
raise ValueError("{} does nothing".format(direction)) | |
for i in range(ROW): | |
if direction == 'a': | |
start = i * ROW | |
end = start + ROW | |
rows[start:end] = shake_row(board[start:end], max) | |
elif direction == 'd': | |
start = i * ROW - 1 | |
end = start + ROW | |
# -1 is the last element and not the one before the first one. | |
# None must be used instead. | |
if start == -1: | |
start = None | |
rows[end:start:-1] = shake_row(board[end:start:-1], max) | |
elif direction in 'w': | |
rows[i::ROW] = shake_row(board[i::ROW], max) | |
elif direction in 's': | |
start = (ROW - 1) * ROW + i | |
rows[start::-ROW] = shake_row(board[start::-ROW], max) | |
return tuple(rows) | |
def shake_row(row, max=2048): | |
""" | |
Shake a given row towards its beginning. | |
Return a new row. | |
""" | |
l = len(row) | |
row = [i for i in row if i != 0] | |
m = 2 | |
while m < max: | |
indices = tuple(all_indices(row, m)) | |
n = m*2 | |
for i in indices[:-1]: | |
if row[i] == row[i+1] == m: | |
row[i] = n | |
row[i+1] = None | |
row = [i for i in row if i is not None] | |
m = n | |
row.extend([0] * (l - len(row))) | |
return tuple(row) | |
def all_indices(haystack, needle): | |
"""Like list.index but for all the indices.""" | |
try: | |
indice = -1 | |
while True: | |
indice = haystack.index(needle, indice+1) | |
yield indice | |
except ValueError: | |
pass | |
def cli(): | |
"""Play the CLI version of the game.""" | |
board = tuple([0] * ROW**2) | |
board = round(round(board)) | |
s = "w" | |
while s in "wasd": | |
show(board) | |
s = input("Shake the board with WASD: ").lower() | |
board = shake(board, s) | |
board = round(board) | |
def main(): | |
"""Play the Qt verson of the game.""" | |
app = QtWidgets.QApplication(sys.argv) | |
window = QtWidgets.QWidget() | |
grid = QtWidgets.QGridLayout() | |
labels = [] | |
for i in range(ROW**2): | |
label = QtWidgets.QLabel("0") | |
label.setAlignment(QtCore.Qt.AlignCenter) | |
labels.append(label) | |
grid.addWidget(label, i // ROW, i % ROW) | |
window.setLayout(grid) | |
board = tuple([0] * ROW**2) | |
board = round(round(board)) | |
keys = { | |
QtCore.Qt.Key_Up: "w", | |
QtCore.Qt.Key_Left: "a", | |
QtCore.Qt.Key_Down: "s", | |
QtCore.Qt.Key_Right: "d", | |
} | |
def update(): | |
"""Update the state of the board.""" | |
for i, v in enumerate(board): | |
labels[i].setText(str(v)) | |
x = math.log(max(1, v) / math.log(2)) | |
font_size = (1.5)**x + 20 | |
color = x / 10 * 360 | |
labels[i].setStyleSheet( | |
"font-size : {0:0.0f}px;" | |
"background-color : hsl({1:0.0f}, 100%, 50%);" | |
"min-width: 120px;" | |
"min-height: 120px;" | |
.format(font_size, color) | |
) | |
window.update() | |
def key_press(event): | |
"""React to a pressed key by playing.""" | |
nonlocal board | |
key = event.key() | |
try: | |
w = keys[key] if key in keys else chr(key).lower() | |
except ValueError: | |
print("Key unknown {}".format(key), file=sys.stderr) | |
return | |
try: | |
m = 4 | |
while m < 2048: | |
new_board = shake(board, w, m) | |
if new_board != board: | |
board = new_board | |
time.sleep(.1) | |
update() | |
app.processEvents() | |
else: | |
board = new_board | |
m *= 2 | |
update() | |
app.processEvents() | |
time.sleep(.1) | |
try: | |
board = round(board) | |
except IndexError: | |
msg_box = QtWidgets.QMessageBox() | |
msg_box.setText("You've lost!") | |
msg_box.exec_() | |
board = tuple([0] * ROW**2) | |
board = round(round(board)) | |
update() | |
app.processEvents() | |
except ValueError: | |
pass | |
update() | |
window.keyPressEvent = key_press | |
window.show() | |
app.exec_() | |
if __name__ == "__main__": | |
try: | |
from PyQt5 import QtWidgets, QtCore | |
main() | |
except ImportError: | |
try: | |
from PyQt4 import QtGui as QtWidgets, QtCore | |
main() | |
except ImportError: | |
print("Install PyQt to play a colorful version of 2048.", | |
"On Anaconda use: `conda install pyqt'\n" | |
"Otherwise: `pip install pyqt5'\n", | |
file=sys.stderr) | |
cli() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment