Article Outline
Example Python program game.py
Modules
- import tkinter
- import math
- import copy
- from tkinter import messagebox
Classes
- class Game( tkinter.Frame ):
Methods
- def init(self, initialize, key_press, should_continue, master = tkinter.Tk(), dimension = 4 ):
- def play(self):
- def _setup_keyboard(self):
- def _pick_color(self, value):
- def _update_gui(self):
- def int_to_str( entry ):
- def keyboard_callback(self, direction ):
- def _build_gui(self):
- def _build_block(self, parent, x, y ):
- def gameover(self):
Code
Python tkinter example
import tkinter
import math
import copy
from tkinter import messagebox
class Game( tkinter.Frame ):
"""
2048 Game class.
Handles keyboard listening and GUI for the 2048 game. Still needs
initialize, key_press and should_continue functions to work properly.
You should not need to change anything in this file.
"""
_tile_size = 128
_background_color = "#cdc1b4"
_colors = [
( "#eee4da", "#776e65" ),
( "#ede0c8", "#776e65" ),
( "#f2b179", "#f9f6f2" ),
( "#f59563", "#f9f6f2" ),
( "#f67c5f", "#f9f6f2" ),
( "#f65e3b", "#f9f6f2" ),
( "#edcf72", "#f9f6f2" ),
( "#edcc61", "#f9f6f2" ),
( "#edc850", "#f9f6f2" ),
( "#edc53f", "#f9f6f2" ),
( "#edc22e", "#f9f6f2" ),
( "#3c3a32", "#f9f6f2" )
]
def __init__(self, initialize, key_press, should_continue, master = tkinter.Tk(), dimension = 4 ):
"""
Create a new game object
:param initialize: student provided initialisation function
:param key_press: student provided key_press function
:param should_continue: student provided should_continue function
:param master: Tk master pane
:param dimension: game dimension
:return: new game
"""
tkinter.Frame.__init__( self, master )
self._master = master
self._master.title( "2048" )
self._initialize = initialize
self._key_press = key_press
self._should_continue = should_continue
self._dimension = dimension
def play(self):
"""
Starts the game play.
"""
self._score = 0
self._board = self._initialize( self._dimension )
self._build_gui()
self._setup_keyboard()
self._update_gui()
self._master.mainloop()
def _setup_keyboard(self):
"""
Bind the keyboard listeners. Up, down, left and right keys are bound to
keyboard_callback method.
"""
self.bind_all( "<Up>", lambda e: self.keyboard_callback( e.keysym ) )
self.bind_all( "<Down>", lambda e: self.keyboard_callback( e.keysym ) )
self.bind_all( "<Left>", lambda e: self.keyboard_callback( e.keysym ) )
self.bind_all( "<Right>", lambda e: self.keyboard_callback( e.keysym ) )
def _pick_color(self, value):
"""
Return suitable colors to style a square based on its value
:param value: square's value
:type value: int
:return: tuple with background and foreground color
:rtype ( String, String )
"""
if value is None:
return ( self._background_color, None )
index = int( max( 0, min( len( self._colors ), math.log( value, 2 ) ) - 1 ) )
return self._colors[ index ]
def _update_gui(self):
"""
Updates the GUI by updating all tile values and the score
"""
def int_to_str( entry ):
if entry is None:
return ""
else:
return "%i" % entry
for i in range( self._dimension ):
for j in range( self._dimension ):
colors = self._pick_color( self._board[i][j] )
self._boxes[i][j].config( anchor = "center", text = int_to_str( self._board[i][j] ), bg = colors[0], fg = colors[1], font = "Helvetica 68 bold" )
self._score_widget.config( text = "Score: %i" % self._score )
def keyboard_callback(self, direction ):
"""
Handle a keyboard event
:param direction: Keyboard symbol pressed (Up, Down, Left or Right)
:type direction: String
"""
self._board, self._score = self._key_press( copy.deepcopy( self._board ), self._score, direction )
self._update_gui()
if not self._should_continue( self._board ):
self.gameover()
def _build_gui(self):
"""
Build the GUI
Adds a label for the score. Creates a matrix with labels for all boxes.
The labels of the boxes are wrapped in a frame to ensure they can be made square.
"""
frame = tkinter.Frame( self._master, bg = "#bbada0" )
frame.grid( stick = "nsew" )
self._score_widget = tkinter.Label( frame, text = "Score: 0", font = "Helvetica 72 bold", fg = "#776e6d", bg = "#faf8ef" )
self._score_widget.grid( row = 0, column = 0, columnspan = self._dimension, sticky = tkinter.W + tkinter.E + tkinter.S + tkinter.N, padx = 10, pady = 10 )
self._boxes = []
for i in range( self._dimension ):
row = []
for j in range( self._dimension ):
row.append( self._build_block( frame, i, j ) )
self._boxes.append( row )
def _build_block(self, parent, x, y ):
"""
Build the GUI block at column x and row y.
:param parent: parent frame
:param x: column
:type x: int
:param y: row
:type y: int
:return: label, wrapped in a frame positioned at x, y + 1 in the grid
:rtype: tkinter.Label
"""
frame = tkinter.Frame( parent, width = self._tile_size, height = self._tile_size )
label = tkinter.Label( frame, text = "0", background = self._background_color )
frame.grid_propagate( False )
frame.columnconfigure( 0, weight = 1 )
frame.rowconfigure(0, weight = 1 )
frame.grid( row = y + 1, column = x, padx = 10, pady = 10 )
label.grid( sticky = "wens" )
return label
def gameover(self):
"""
Indicate to the user the game is over and quit the programm.
"""
messagebox.showinfo( "Game over", "No more moves left! You reached a score of %i" % self._score )
self.quit()
Useful Links
- Articles: https://python-commandments.org/
- Python shell: https://bsdnerds.org/learn-python/
- Tutorial: https://pythonprogramminglanguage.com/