#!/usr/bin/env PYTHONHASHSEED=1234 python3 # Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Reproduce book environment import random random.seed(1234) import logging from pprint import pprint from sys import stdout as STDOUT # Write all output to a temporary directory import atexit import gc import io import os import tempfile TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) # Make sure Windows processes exit cleanly OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() atexit.register(close_open_files) # Example 1 ALIVE = '*' EMPTY = '-' class Grid: def __init__(self, height, width): self.height = height self.width = width self.rows = [] for _ in range(self.height): self.rows.append([EMPTY] * self.width) def get(self, y, x): return self.rows[y % self.height][x % self.width] def set(self, y, x, state): self.rows[y % self.height][x % self.width] = state def __str__(self): output = '' for row in self.rows: for cell in row: output += cell output += '\n' return output def count_neighbors(y, x, get): n_ = get(y - 1, x + 0) # North ne = get(y - 1, x + 1) # Northeast e_ = get(y + 0, x + 1) # East se = get(y + 1, x + 1) # Southeast s_ = get(y + 1, x + 0) # South sw = get(y + 1, x - 1) # Southwest w_ = get(y + 0, x - 1) # West nw = get(y - 1, x - 1) # Northwest neighbor_states = [n_, ne, e_, se, s_, sw, w_, nw] count = 0 for state in neighbor_states: if state == ALIVE: count += 1 return count async def game_logic(state, neighbors): # Do some input/output in here: data = await my_socket.read(50) async def game_logic(state, neighbors): if state == ALIVE: if neighbors < 2: return EMPTY # Die: Too few elif neighbors > 3: return EMPTY # Die: Too many else: if neighbors == 3: return ALIVE # Regenerate return state # Example 2 async def step_cell(y, x, get, set): state = get(y, x) neighbors = count_neighbors(y, x, get) next_state = await game_logic(state, neighbors) set(y, x, next_state) # Example 3 import asyncio async def simulate(grid): next_grid = Grid(grid.height, grid.width) tasks = [] for y in range(grid.height): for x in range(grid.width): task = step_cell( y, x, grid.get, next_grid.set) # Fan out tasks.append(task) await asyncio.gather(*tasks) # Fan in return next_grid # Example 4 class ColumnPrinter: def __init__(self): self.columns = [] def append(self, data): self.columns.append(data) def __str__(self): row_count = 1 for data in self.columns: row_count = max( row_count, len(data.splitlines()) + 1) rows = [''] * row_count for j in range(row_count): for i, data in enumerate(self.columns): line = data.splitlines()[max(0, j - 1)] if j == 0: padding = ' ' * (len(line) // 2) rows[j] += padding + str(i) + padding else: rows[j] += line if (i + 1) < len(self.columns): rows[j] += ' | ' return '\n'.join(rows) logging.getLogger().setLevel(logging.ERROR) grid = Grid(5, 9) grid.set(0, 3, ALIVE) grid.set(1, 4, ALIVE) grid.set(2, 2, ALIVE) grid.set(2, 3, ALIVE) grid.set(2, 4, ALIVE) columns = ColumnPrinter() for i in range(5): columns.append(str(grid)) grid = asyncio.run(simulate(grid)) # Run the event loop print(columns) logging.getLogger().setLevel(logging.DEBUG) # Example 5 try: async def game_logic(state, neighbors): raise OSError('Problem with I/O') logging.getLogger().setLevel(logging.ERROR) asyncio.run(game_logic(ALIVE, 3)) logging.getLogger().setLevel(logging.DEBUG) except: logging.exception('Expected') else: assert False # Example 6 async def count_neighbors(y, x, get): n_ = get(y - 1, x + 0) # North ne = get(y - 1, x + 1) # Northeast e_ = get(y + 0, x + 1) # East se = get(y + 1, x + 1) # Southeast s_ = get(y + 1, x + 0) # South sw = get(y + 1, x - 1) # Southwest w_ = get(y + 0, x - 1) # West nw = get(y - 1, x - 1) # Northwest neighbor_states = [n_, ne, e_, se, s_, sw, w_, nw] count = 0 for state in neighbor_states: if state == ALIVE: count += 1 return count async def step_cell(y, x, get, set): state = get(y, x) neighbors = await count_neighbors(y, x, get) next_state = await game_logic(state, neighbors) set(y, x, next_state) async def game_logic(state, neighbors): if state == ALIVE: if neighbors < 2: return EMPTY # Die: Too few elif neighbors > 3: return EMPTY # Die: Too many else: if neighbors == 3: return ALIVE # Regenerate return state logging.getLogger().setLevel(logging.ERROR) grid = Grid(5, 9) grid.set(0, 3, ALIVE) grid.set(1, 4, ALIVE) grid.set(2, 2, ALIVE) grid.set(2, 3, ALIVE) grid.set(2, 4, ALIVE) columns = ColumnPrinter() for i in range(5): columns.append(str(grid)) grid = asyncio.run(simulate(grid)) print(columns) logging.getLogger().setLevel(logging.DEBUG)