The following word game by Omz was in the examples directory of old version of Pythonista. I have modified it slightly to run on new version (python 3).
# coding: utf-8
from scene import *
import ui
import sound
import marshal
import string
import time
from itertools import product, chain
from random import choice, random
from copy import copy
import os
from math import sqrt
import json
A = Action
game_duration = 90
screen_w, screen_h = get_screen_size()
min_screen = min(screen_w, screen_h)
if max(get_screen_size()) >= 760:
cols, rows = 10, 11
else:
cols, rows = 7, 7
tile_size = min_screen / (max(cols, rows) + 1)
font_size = int(tile_size * 0.6)
tile_font = ('AvenirNext-Regular', font_size)
score_font = ('AvenirNext-Regular', 50)
time_font = ('AvenirNext-Regular', 32)
preview_font = ('AvenirNext-Regular', 24)
game_over_font = ('AvenirNext-Regular', 72)
points_font = ('AvenirNextCondensed-Regular', 32)
# Derived from http://en.m.wikipedia.org/wiki/Letter_frequency
letter_freq = {'a': 8.2, 'b': 1.5, 'c': 2.8, 'd': 4.3, 'e': 12.7, 'f': 2.3, 'g': 2.0, 'h': 6.1, 'i': 7.0, 'j': 0.2, 'k': 7.7, 'l': 4.0, 'm': 2.4, 'n': 6.7, 'o': 7.5, 'p': 1.9, 'q': 0.1, 'r': 6.0, 's': 6.3, 't': 9.0, 'u': 2.8, 'v': 1.0, 'w': 2.4, 'x': 0.2, 'y': 2.0, 'z': 0.1}
letter_bag = list(chain(*[[letter] * int(letter_freq[letter]*10) for letter in letter_freq]))
def build_dictionary():
# Generate the word list if it doesn't exist yet.
# It's represented as a set for fast lookup, and saved to disk using the `marshal` module.
if os.path.exists('words.data'):
return
#import urllib
import requests
words = []
#f = urllib.urlopen('https://github.com/atebits/Words/blob/master/Words/en.txt?raw=true')
f = str(requests.get('https://github.com/atebits/Words/blob/master/Words/en.txt?raw=true').text)
for line in f.split():
words.append(line.strip())
with open('words.data', 'w') as out:
#marshal.dump(words, out)
json.dump(words, out)
with ui.ImageContext(tile_size, tile_size) as ctx:
ui.set_color('silver')
ui.Path.rounded_rect(2, 2, tile_size-4, tile_size-4, 4).fill()
ui.set_color('white')
ui.Path.rounded_rect(2, 2, tile_size-4, tile_size-6, 4).fill()
tile_texture = Texture(ctx.get_image())
class Tile (SpriteNode):
def __init__(self, x, y, letter, color='white', multiplier=1):
SpriteNode.__init__(self, tile_texture)
self.x = x
self.y = y
self.letter = letter
self._selected = False
pos_y = y * tile_size + (tile_size/2 if x % 2 == 0 else 0)
self.position = x * tile_size, pos_y
self.tile_color = color
self.color = color
self.label = LabelNode(letter.upper(), font=tile_font)
self.label.color = 'black'
self.multiplier = multiplier
self.add_child(self.label)
@property
def selected(self):
return self._selected
@selected.setter
def selected(self, value):
self._selected = value
self.color = '#fdffce' if value else self.tile_color
class Game (Scene):
def setup(self):
self.current_size = None
self.current_word = None
build_dictionary()
with open('words.data') as f:
#self.words = marshal.load(f)
self.words = set(json.load(f))
self.root = Node(parent=self)
self.background_color = '#0f2634'
self.tiles = []
self.selected = []
self.touched_tile = None
self.score_label = LabelNode('0', font=score_font, parent=self)
self.score = 0
self.game_over = False
self.game_over_time = 0
self.word_label = LabelNode(font=preview_font, parent=self)
self.time_label = LabelNode('00:00', font=time_font, parent=self)
self.time_label.anchor_point = (0, 0.5)
self.start_time = time.time()
self.overlay = SpriteNode(color='black', alpha=0, z_position=3, parent=self)
time_up_label = LabelNode('Time Up!', font=game_over_font, parent=self.overlay)
self.did_change_size()
self.new_game()
def create_tile(self, x, y):
letter = choice(letter_bag)
bonus = random() < 0.07
t = Tile(x, y, letter, '#cef9ff' if bonus else 'white', 2 if bonus else 1)
return t
def did_change_size(self):
x_margin = (self.size.w - cols * tile_size)/2
y_margin = (self.size.h - rows * tile_size)/2 - tile_size/2
self.root.position = x_margin + tile_size/2, y_margin + tile_size/2
self.overlay.position = self.size/2
self.overlay.size = self.size
if self.size.w < self.size.h:
self.score_label.position = self.size.w/2, self.size.h - 100
self.word_label.position = self.size.w/2, self.size.h - 140
self.time_label.position = 20, self.size.h - 100
self.time_label.anchor_point = (0, 0.5)
else:
self.score_label.position = x_margin/2, self.size.h - 100
self.word_label.position = x_margin/2, self.size.h - 140
self.time_label.position = self.size - (20, 100)
self.time_label.anchor_point = (1, 0.5)
def update(self):
time_passed = time.time() - self.start_time
t = max(0, int(game_duration - time_passed))
self.time_label.text = '{0}:{1:0>2}'.format(t//60, t%60)
if t == 0 and not self.game_over:
self.end_game()
def new_game(self, animated=False):
if self.game_over:
self.play_sound('digital:ZapThreeToneUp')
for tile in self.tiles:
tile.remove_from_parent()
self.tiles = []
for x, y in product(range(cols), range(rows)):
s = self.create_tile(x, y)
if animated:
s.scale = 0
s.run_action(Action.scale_to(1, 0.5, TIMING_EASE_OUT_2))
self.tiles.append(s)
self.root.add_child(s)
self.game_over = False
self.start_time = time.time()
self.score = 0
self.score_label.text = '0'
self.overlay.run_action(Action.fade_to(0))
self.word_label.text = ''
self.selected = []
def end_game(self):
self.play_sound('digital:ZapThreeToneDown')
self.game_over = True
self.game_over_time = time.time()
self.overlay.run_action(Action.fade_to(0.7))
def get_selected_word(self):
return ''.join([t.letter for t in self.selected]).lower()
def touch_to_tile(self, location):
touch_x = location[0] - self.root.position[0]
touch_y = location[1] - self.root.position[1]
x = int(touch_x / tile_size)
if x % 2 != 0:
y = int((touch_y - tile_size/2) / tile_size)
else:
y = int(touch_y / tile_size)
return x, y
def tile_at(self, location):
x = location[0] - self.root.position[0]
y = location[1] - self.root.position[1]
for tile in self.tiles:
if abs(tile.position - (x, y)) < tile_size/2:
if tile.alpha < 1:
continue
return tile
return None
def is_neighbor(self, tile1, tile2):
if (not tile1 or not tile2 or tile1 == tile2):
return True
x1, y1 = tile1.x, tile1.y
x2, y2 = tile2.x, tile2.y
if x1 == x2:
return abs(y2-y1) <= 1
elif x1 % 2 == 0:
return abs(x2-x1) <= 1 and 0 <= (y2-y1) <= 1
else:
return abs(x2-x1) <= 1 and -1 <= (y2-y1) <= 0
def select_tile(self, tile):
if not tile or (self.selected and self.selected[-1] == tile):
return
if tile in self.selected:
self.selected = self.selected[:self.selected.index(tile)+1]
else:
if self.selected:
last_selected = self.selected[-1]
if not self.is_neighbor(tile, last_selected):
self.selected = []
self.selected.append(tile)
else:
self.selected = [tile]
for tile in self.tiles:
tile.selected = (tile in self.selected)
self.play_sound('8ve:8ve-tap-resonant')
self.update_word_label()
def update_word_label(self):
word = self.get_selected_word()
if word != self.current_word:
self.word_label.color = '#ddffc2' if word in self.words else '#ffcece'
self.word_label.text = word.upper()
self.current_word = word
def calc_score(self, word_tiles):
n = len(word_tiles)
multiplier = 1
for tile in word_tiles:
multiplier *= tile.multiplier
return int((2 ** (n-2)) * 50 * multiplier)
def submit_word(self):
word = self.get_selected_word()
if not word:
return
if word in self.words:
added_score = self.calc_score(self.selected)
self.score += added_score
self.score_label.text = str(self.score)
self.play_sound('digital:PowerUp7')
else:
added_score = 0
self.play_sound('digital:PepSound4')
for tile in self.selected:
tile.selected = False
self.selected = []
self.touched_tile = None
for tile in self.selected:
tile.run_action(A.group(A.fade_to(0, 0.25), A.scale_to(0.5, 0.25)))
self.tiles[:] = [t for t in self.tiles if t not in self.selected]
sorted_selection = sorted(self.selected, key=lambda t: t.y, reverse=True)
new_tiles_by_col = [0] * cols
offsets = [0] * len(self.tiles)
for t in sorted_selection:
x, y = t.x, t.y
new_tiles_by_col[x] += 1
for i, tile in enumerate(self.tiles):
if tile.x == x and tile.y > y:
tile.y -= 1
offsets[i] += 1
for i, offset in enumerate(offsets):
if offset > 0:
tile = self.tiles[i]
d = sqrt(offset * tile_size/750.0)
tile.run_action(A.move_by(0, -offset*tile_size, d, TIMING_EASE_IN_2))
for i, n in enumerate(new_tiles_by_col):
for j in range(n):
s = self.create_tile(i, rows-j-1)
to_pos = s.position
from_pos = to_pos[0], (rows + n-j) * tile_size
s.position = from_pos
self.tiles.append(s)
self.root.add_child(s)
s.alpha = 0
s.run_action(Action.fade_to(1, 0.25))
s.run_action(Action.move_to(to_pos[0], to_pos[1], sqrt((from_pos[1] - to_pos[1])/750.0), TIMING_EASE_IN_2))
if added_score > 0:
self.show_points(self.selected[-1].position, added_score)
self.selected = []
self.touched_tile = None
self.update_word_label()
def show_points(self, position, added_score):
points_bg = ShapeNode(ui.Path.oval(0, 0, 100, 100), '#49b8ff', alpha=0)
points_bg.position = position
points_label = LabelNode('+%i' % (added_score,), font=points_font, parent=points_bg)
points_bg.run_action(A.sequence(
A.fade_to(1, 0.25),
A.wait(0.5),
A.fade_to(0, 0.5, TIMING_EASE_IN),
A.remove()
))
points_bg.run_action(Action.move_by(0, 100, 1.5))
self.root.add_child(points_bg)
def touch_began(self, touch):
if self.game_over:
return
prev_touched_tile = self.touched_tile
self.touched_tile = self.tile_at(touch.location)
if prev_touched_tile == self.touched_tile:
self.submit_word()
elif self.touched_tile:
self.select_tile(self.touched_tile)
def touch_moved(self, touch):
if not self.game_over:
self.select_tile(self.tile_at(touch.location))
def touch_ended(self, touch):
if self.game_over:
if time.time() - self.game_over_time > 2.0:
self.new_game(animated=True)
return
tile = self.tile_at(touch.location)
if tile == self.touched_tile:
self.select_tile(tile)
else:
self.submit_word()
def play_sound(self, name):
sound.play_effect(name)
if __name__ == '__main__':
run(Game(), multi_touch=False)