From b0a730d48218be937fba665588484fde322d1030 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Sun, 15 Jan 2017 19:47:13 +0100 Subject: [PATCH] nrf5/examples: Adding 2048 game using OLED SSD1306 128x64 display and analog joystick. --- nrf5/examples/game2048.py | 348 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 nrf5/examples/game2048.py diff --git a/nrf5/examples/game2048.py b/nrf5/examples/game2048.py new file mode 100644 index 0000000000..2533dc06de --- /dev/null +++ b/nrf5/examples/game2048.py @@ -0,0 +1,348 @@ +# This file is part of the Micro Python project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2017 Glenn Ruben Bakke +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +''' +Usage: + +from game2048 import Game +g = Game() +g.start() + +''' + +from machine import ADC +from machine import Pin, SPI +from display import SSD1306 +import draw +import time + +font_128 = [ + " x xx xx", + " xx x x x x", + "x x x x x", + " x x xx", + " x x x x", + " x x x x", + " xxx xxxx xx" +] + +font_256 = [ + " xx xxxx xx", + "x x x x x", + " x xxx x ", + " x x xxxx", + " x x x x", + "x x x x", + "xxxx xxx xx" +] + +font_512 = [ + "xxxx x xx", + "x xx x x", + "xxx x x x", + " x x x", + " x x x", + " x x x", + "xxx xxx xxxx" +] + +font_1024 = [ + " xx x x x x", + "x x x x x x x x", + " x x x x x x", + " x x x x xxx", + " x x x x x", + " x x x x x", + " x x xxx x" +] + +font_2048 = [ + " x x x x x", + "x x x x x x x x", + " x x x x x x x", + " x x x xxx x", + "x x x x x x", + "x x x x x x", + "xxx x x x" +] + +DIR_UP = const(0x1) +DIR_DOWN = const(0x2) +DIR_LEFT = const(0x3) +DIR_RIGHT = const(0x4) +DIR_CENTER = const(0x5) + +class Game: + def __init__(self): + +# # setup harware for nrf51822 / pca10028 +# cs = Pin("A14", mode=Pin.OUT, pull=Pin.PULL_UP) +# reset = Pin("A13", mode=Pin.OUT, pull=Pin.PULL_UP) +# dc = Pin("A12", mode=Pin.OUT, pull=Pin.PULL_UP) +# spi = SPI(0, baudrate=8000000) +# self.screen = SSD1306(128, 64, spi, cs, dc, reset) +# self.x_adc = ADC(2) +# self.y_adc = ADC(3) +# self.adc_threshold = 205 + + # setup harware for nrf52840 / pca10056 + cs = Pin("B3", mode=Pin.OUT, pull=Pin.PULL_UP) + reset = Pin("B2", mode=Pin.OUT, pull=Pin.PULL_UP) + dc = Pin("B1", mode=Pin.OUT, pull=Pin.PULL_UP) + spi = SPI(0, baudrate=8000000) + self.screen = SSD1306(128, 64, spi, cs, dc, reset) + self.x_adc = ADC(1) + self.y_adc = ADC(2) + self.adc_threshold = 130 + + # game setup + self.grid = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] + self._rand = 1337 + self.not_game_over = True + self.score = 0 + + + def move(self, dir): + if (dir == DIR_UP): + # float up + for col in range(0, 4): + for row1 in range(1, 5): + for row0 in range(1, row1): + if self.grid[col][row0 - 1] == 0: + if self.grid[col][row0]: + self.grid[col][row0 - 1] = self.grid[col][row0] + self.grid[col][row0] = 0 + # collapse + for col in range(0, 4): + for row0 in range(0, 3): + if self.grid[col][row0] == self.grid[col][row0 + 1]: + self.grid[col][row0] += self.grid[col][row0] + self.score += self.grid[col][row0] + self.grid[col][row0 + 1] = 0 + # float up + for col in range(0, 4): + for row1 in range(1, 5): + for row0 in range(1, row1): + if self.grid[col][row0 - 1] == 0: + if self.grid[col][row0]: + self.grid[col][row0 - 1] = self.grid[col][row0] + self.grid[col][row0] = 0 + elif (dir == DIR_DOWN): + # float down + for col in range(0, 4): + for row1 in range(1, 5): + for row0 in range(row1, 4): + if self.grid[col][row0] == 0: + if self.grid[col][row0 - 1]: + self.grid[col][row0] = self.grid[col][row0 - 1] + self.grid[col][row0 - 1] = 0 + # collapse + for col in range(0, 4): + for row0 in range(1, 4): + if self.grid[col][row0] == self.grid[col][row0 - 1]: + self.grid[col][row0] += self.grid[col][row0] + self.score += self.grid[col][row0] + self.grid[col][row0 - 1] = 0 + # float down + for col in range(0, 4): + for row1 in range(1, 5): + for row0 in range(row1, 4): + if self.grid[col][row0] == 0: + if self.grid[col][row0 - 1]: + self.grid[col][row0] = self.grid[col][row0 - 1] + self.grid[col][row0 - 1] = 0 + elif (dir == DIR_LEFT): + # float left + for row in range(0, 4): + for col1 in range(1, 5): + for col0 in range(1, col1): + if self.grid[col0 - 1][row] == 0: + if self.grid[col0][row]: + self.grid[col0 - 1][row] = self.grid[col0][row] + self.grid[col0][row] = 0 + # collapse + for row in range(0, 4): + for col0 in range(0, 3): + if self.grid[col0][row] == self.grid[col0 + 1][row]: + self.grid[col0][row] += self.grid[col0][row] + self.score += self.grid[col0][row] + self.grid[col0 + 1][row] = 0 + # float left + for row in range(0, 4): + for col1 in range(1, 5): + for col0 in range(1, col1): + if self.grid[col0 - 1][row] == 0: + if self.grid[col0][row]: + self.grid[col0 - 1][row] = self.grid[col0][row] + self.grid[col0][row] = 0 + elif (dir == DIR_RIGHT): + # float right + for row in range(0, 4): + for col1 in range(1, 5): + for col0 in range(col1, 4): + if self.grid[col0][row] == 0: + if self.grid[col0 - 1][row]: + self.grid[col0][row] = self.grid[col0 - 1][row] + self.grid[col0 - 1][row] = 0 + # collapse + for row in range(0, 4): + for col0 in range(1, 4): + if self.grid[col0][row] == self.grid[col0 - 1][row]: + self.grid[col0][row] += self.grid[col0][row] + self.score += self.grid[col0][row] + self.grid[col0 - 1][row] = 0 + # float right + for row in range(0, 4): + for col1 in range(1, 5): + for col0 in range(col1, 4): + if self.grid[col0][row] == 0: + if self.grid[col0 - 1][row]: + self.grid[col0][row] = self.grid[col0 - 1][row] + self.grid[col0 - 1][row] = 0 + def set_val(self, x, y, val): + self.grid[x][y] = val + def get_val(self, x, y): + return self.grid[x][y] + def draw_high_number(self, font, x, y): + for rel_y, line in enumerate(font): + for rel_x in range(0, len(line)): + if (line[rel_x] == 'x'): + self.screen.pixel(x + rel_x, y + rel_y, 1) + def draw_tile(self, x, y): + val = self.get_val(x, y) + if val != 0: + x0 = x * 16 + y0 = y * 16 + if (val < 16): + draw.rectangle(self.screen, x0, y0, x0 + 15, y0 + 15, 1) + draw.text(self.screen, str(val), x0 + 4, y0 + 4, 1) + elif val == 16: + draw.rectangle(self.screen, x0, y0, x0 + 15, y0 + 15, 1) + draw.text(self.screen, "1", x0 + 1, y0 + 4, 1) + draw.text(self.screen, "6", x0 + 7, y0 + 4, 1) + elif val == 32: + draw.rectangle(self.screen, x0, y0, x0 + 15, y0 + 15, 1) + draw.text(self.screen, "3", x0 + 1, y0 + 4, 1) + draw.text(self.screen, "2", x0 + 7, y0 + 4, 1) + elif val == 64: + draw.rectangle(self.screen, x0, y0, x0 + 15, y0 + 15, 1) + draw.text(self.screen, "6", x0 + 1, y0 + 4, 1) + draw.text(self.screen, "4", x0 + 7, y0 + 4, 1) + elif val == 128: + draw.rectangle(self.screen, x0, y0, x0 + 15, y0 + 15, 1) + self.draw_high_number(font_128, x0 + 1, y0 + 4) + elif val == 256: + draw.rectangle(self.screen, x0, y0, x0 + 15, y0 + 15, 1) + self.draw_high_number(font_256, x0 + 1, y0 + 4) + elif val == 512: + draw.rectangle(self.screen, x0, y0, x0 + 15, y0 + 15, 1) + self.draw_high_number(font_512, x0 + 1, y0 + 4) + elif val == 1024: + draw.rectangle(self.screen, x0, y0, x0 + 15, y0 + 15, 1) + self.draw_high_number(font_1024, x0 + 1, y0 + 4) + elif val == 2048: + draw.rectangle(self.screen, x0, y0, x0 + 15, y0 + 15, 1) + self.draw_high_number(font_2048, x0 + 1, y0 + 4) + def read_stick_x(self): + return self.x_adc.value() + def read_stick_y(self): + return self.y_adc.value() + def wait_for_move(self): + x_val = self.read_stick_x() + y_val = self.read_stick_y() + if x_val > self.adc_threshold + 15: + return DIR_RIGHT + elif x_val < self.adc_threshold - 15: + return DIR_LEFT + if y_val > self.adc_threshold + 15: + return DIR_UP + elif y_val < self.adc_threshold - 15: + return DIR_DOWN + return DIR_CENTER + def get_free_tiles(self): + list = [] + for x in range(0, 4): + for y in range(0, 4): + if self.get_val(x, y) == 0: + list.append((x, y)) + return list + def draw_all_tiles(self): + for x in range(0, 4): + for y in range(0, 4): + self.draw_tile(x, y) + def rand(self, mod=0): + self._rand = ((214013*self._rand+2531011)>>16)&0x7FFF + if mod: + return self._rand % mod + else: + return self._rand + def add_random_tile(self): + free_tiles = self.get_free_tiles() + if free_tiles: + x,y = free_tiles[self.rand(len(free_tiles))] + new_val = 2 << self.rand(2) + self.set_val(x, y, new_val) + return True + else: + return False + def draw_score(self): + draw.text(self.screen, "Score:", 70, 10, 1) + draw.text(self.screen, str(self.score), 70, 20, 1) + def clear_grid(self): + # Because the the double array does not initialize correctly + # in micropython, we do an explicit clear + for i in range(0, 4): + for j in range(0, 4): + self.grid[i][j] = 0 + def start(self): + self.screen.fill(0) + draw.text(self.screen, "Touch the stick", 10, 20, 1) + draw.text(self.screen, " to start the", 10, 30, 1) + draw.text(self.screen, " game! =)", 10, 40, 1) + self.screen.show() + in_center = False + x_move = True + y_move = True + wait_for_center = False + self.clear_grid() + while self.not_game_over: + move_dir = self.wait_for_move() + if move_dir != DIR_CENTER and wait_for_center == False: + self.move(move_dir) + self.screen.fill(0) + res = self.add_random_tile() + if not res: + self.not_game_over = False + else: + self.draw_all_tiles() + self.draw_score() + self.screen.show() + wait_for_center = True + if move_dir == DIR_CENTER: + wait_for_center = False + self.screen.fill(0) + draw.text(self.screen, "Game Over!", 10, 20, 1) + draw.text(self.screen, "Score:", 10, 30, 1) + draw.text(self.screen, str(self.score), 10, 40, 1) + self.screen.show() \ No newline at end of file