-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Game of Life source code * TR updates and step folders * Fix format issues * Format issue * DR updates, first round * framerate -> frame_rate * Removed duplicate frame_rate and bbox * frame_rate in __main__.py * README LE * Link to tutorial --------- Co-authored-by: KateFinegan <[email protected]>
- Loading branch information
1 parent
735dd06
commit b838b47
Showing
65 changed files
with
1,949 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# `rplife` | ||
|
||
Conway's Game of Life in your terminal, to accompany the Real Python tutorial [Build Conway's Game of Life With Python](https://realpython.com/conway-game-of-life-python/). | ||
|
||
## Installation | ||
|
||
1. Create and activate a Python virtual environment: | ||
|
||
```sh | ||
$ python -m venv ./venv | ||
$ source venv/bin/activate | ||
(venv) $ | ||
``` | ||
|
||
2. Install `rplife` in editable mode: | ||
|
||
```sh | ||
(venv) $ cd rplife | ||
(venv) $ pip install -e . | ||
``` | ||
|
||
## Execution | ||
|
||
To execute `rplife`, go ahead and run the following command: | ||
|
||
```sh | ||
(venv) $ rplife -a | ||
``` | ||
|
||
## Author | ||
|
||
Real Python - Email: [email protected] | ||
|
||
## License | ||
|
||
Distributed under the MIT license. See `LICENSE` for more information. |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[build-system] | ||
requires = ["setuptools>=64.0.0", "wheel"] | ||
build-backend = "setuptools.build_meta" | ||
|
||
[project] | ||
name = "rplife" | ||
dynamic = ["version"] | ||
description = "Conway's Game of Life in your terminal" | ||
readme = "README.md" | ||
authors = [{ name = "Real Python", email = "[email protected]" }] | ||
dependencies = [ | ||
'tomli; python_version < "3.11"', | ||
] | ||
|
||
[project.scripts] | ||
rplife = "rplife.__main__:main" | ||
|
||
[tool.setuptools.dynamic] | ||
version = {attr = "rplife.__version__"} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__version__ = "1.0.0" |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import sys | ||
|
||
from rplife import patterns, views | ||
from rplife.cli import get_command_line_args | ||
|
||
|
||
def main(): | ||
args = get_command_line_args() | ||
View = getattr(views, args.view) | ||
if args.all: | ||
for pattern in patterns.get_all_patterns(): | ||
_show_pattern(View, pattern, args) | ||
else: | ||
_show_pattern(View, patterns.get_pattern(name=args.pattern), args) | ||
|
||
|
||
def _show_pattern(View, pattern, args): | ||
try: | ||
View(pattern=pattern, gen=args.gen, frame_rate=args.fps).show() | ||
except Exception as error: | ||
print(error, file=sys.stderr) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import argparse | ||
|
||
from rplife import __version__, patterns, views | ||
|
||
|
||
def get_command_line_args(): | ||
parser = argparse.ArgumentParser( | ||
prog="rplife", | ||
description="Conway's Game of Life in your terminal", | ||
) | ||
parser.add_argument( | ||
"--version", action="version", version=f"%(prog)s v{__version__}" | ||
) | ||
parser.add_argument( | ||
"-p", | ||
"--pattern", | ||
choices=[pat.name for pat in patterns.get_all_patterns()], | ||
default="Blinker", | ||
help="take a pattern for the Game of Life (default: %(default)s)", | ||
) | ||
parser.add_argument( | ||
"-a", | ||
"--all", | ||
action="store_true", | ||
help="show all available patterns in a sequence", | ||
) | ||
parser.add_argument( | ||
"-v", | ||
"--view", | ||
choices=views.__all__, | ||
default="CursesView", | ||
help="display the life grid in a specific view (default: %(default)s)", | ||
) | ||
parser.add_argument( | ||
"-g", | ||
"--gen", | ||
metavar="NUM_GENERATIONS", | ||
type=int, | ||
default=10, | ||
help="number of generations (default: %(default)s)", | ||
) | ||
parser.add_argument( | ||
"-f", | ||
"--fps", | ||
metavar="FRAMES_PER_SECOND", | ||
type=int, | ||
default=7, | ||
help="frames per second (default: %(default)s)", | ||
) | ||
return parser.parse_args() |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import collections | ||
|
||
ALIVE = "♥" | ||
DEAD = "‧" | ||
|
||
|
||
class LifeGrid: | ||
def __init__(self, pattern): | ||
self.pattern = pattern | ||
|
||
def evolve(self): | ||
neighbors = ( | ||
(-1, -1), # Above left | ||
(-1, 0), # Above | ||
(-1, 1), # Above right | ||
(0, -1), # Left | ||
(0, 1), # Right | ||
(1, -1), # Below left | ||
(1, 0), # Below | ||
(1, 1), # Below right | ||
) | ||
num_neighbors = collections.defaultdict(int) | ||
for row, col in self.pattern.alive_cells: | ||
for drow, dcol in neighbors: | ||
num_neighbors[(row + drow, col + dcol)] += 1 | ||
|
||
stay_alive = { | ||
cell for cell, num in num_neighbors.items() if num in {2, 3} | ||
} & self.pattern.alive_cells | ||
come_alive = { | ||
cell for cell, num in num_neighbors.items() if num == 3 | ||
} - self.pattern.alive_cells | ||
|
||
self.pattern.alive_cells = stay_alive | come_alive | ||
|
||
def as_string(self, bbox): | ||
start_col, start_row, end_col, end_row = bbox | ||
display = [self.pattern.name.center(2 * (end_col - start_col))] | ||
for row in range(start_row, end_row): | ||
display_row = [ | ||
ALIVE if (row, col) in self.pattern.alive_cells else DEAD | ||
for col in range(start_col, end_col) | ||
] | ||
display.append(" ".join(display_row)) | ||
return "\n ".join(display) | ||
|
||
def __str__(self): | ||
return ( | ||
f"{self.pattern.name}:\n" | ||
f"Alive cells -> {sorted(self.pattern.alive_cells)}" | ||
) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from dataclasses import dataclass | ||
from pathlib import Path | ||
|
||
try: | ||
import tomllib | ||
except ImportError: | ||
import tomli as tomllib | ||
|
||
PATTERNS_FILE = Path(__file__).parent / "patterns.toml" | ||
|
||
|
||
@dataclass | ||
class Pattern: | ||
name: str | ||
alive_cells: set[tuple[int, int]] | ||
|
||
@classmethod | ||
def from_toml(cls, name, toml_data): | ||
return cls( | ||
name, | ||
alive_cells={tuple(cell) for cell in toml_data["alive_cells"]}, | ||
) | ||
|
||
|
||
def get_pattern(name, filename=PATTERNS_FILE): | ||
data = tomllib.loads(filename.read_text(encoding="utf-8")) | ||
return Pattern.from_toml(name, toml_data=data[name]) | ||
|
||
|
||
def get_all_patterns(filename=PATTERNS_FILE): | ||
data = tomllib.loads(filename.read_text(encoding="utf-8")) | ||
return [ | ||
Pattern.from_toml(name, toml_data) for name, toml_data in data.items() | ||
] |
142 changes: 142 additions & 0 deletions
142
game-of-life-python/source_code_final/rplife/patterns.toml
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
["Blinker"] | ||
alive_cells = [[2, 1], [2, 2], [2, 3]] | ||
|
||
["Toad"] | ||
alive_cells = [[2, 2], [2, 3], [2, 4], [3, 1], [3, 2], [3, 3]] | ||
|
||
["Beacon"] | ||
alive_cells = [[1, 1], [1, 2], [2, 1], [4, 3], [4, 4], [3, 4]] | ||
|
||
["Pulsar"] | ||
alive_cells = [ | ||
[2, 4], | ||
[2, 5], | ||
[2, 6], | ||
[2, 10], | ||
[2, 11], | ||
[2, 12], | ||
[4, 2], | ||
[5, 2], | ||
[6, 2], | ||
[4, 7], | ||
[5, 7], | ||
[6, 7], | ||
[4, 9], | ||
[5, 9], | ||
[6, 9], | ||
[4, 14], | ||
[5, 14], | ||
[6, 14], | ||
[7, 4], | ||
[7, 5], | ||
[7, 6], | ||
[7, 10], | ||
[7, 11], | ||
[7, 12], | ||
[9, 4], | ||
[9, 5], | ||
[9, 6], | ||
[9, 10], | ||
[9, 11], | ||
[9, 12], | ||
[10, 2], | ||
[11, 2], | ||
[12, 2], | ||
[10, 7], | ||
[11, 7], | ||
[12, 7], | ||
[10, 9], | ||
[11, 9], | ||
[12, 9], | ||
[10, 14], | ||
[11, 14], | ||
[12, 14], | ||
[14, 4], | ||
[14, 5], | ||
[14, 6], | ||
[14, 10], | ||
[14, 11], | ||
[14, 12] | ||
] | ||
|
||
["Penta Decathlon"] | ||
alive_cells = [ | ||
[5, 4], | ||
[6, 4], | ||
[7, 4], | ||
[8, 4], | ||
[9, 4], | ||
[10, 4], | ||
[11, 4], | ||
[12, 4], | ||
[5, 5], | ||
[7, 5], | ||
[8, 5], | ||
[9, 5], | ||
[10, 5], | ||
[12, 5], | ||
[5, 6], | ||
[6, 6], | ||
[7, 6], | ||
[8, 6], | ||
[9, 6], | ||
[10, 6], | ||
[11, 6], | ||
[12, 6] | ||
] | ||
|
||
["Glider"] | ||
alive_cells = [[0, 2], [1, 0], [1, 2], [2, 1], [2, 2]] | ||
|
||
["Glider Gun"] | ||
alive_cells = [ | ||
[0, 24], | ||
[1, 22], | ||
[1, 24], | ||
[2, 12], | ||
[2, 13], | ||
[2, 20], | ||
[2, 21], | ||
[2, 34], | ||
[2, 35], | ||
[3, 11], | ||
[3, 15], | ||
[3, 20], | ||
[3, 21], | ||
[3, 34], | ||
[3, 35], | ||
[4, 0], | ||
[4, 1], | ||
[4, 10], | ||
[4, 16], | ||
[4, 20], | ||
[4, 21], | ||
[5, 0], | ||
[5, 1], | ||
[5, 10], | ||
[5, 14], | ||
[5, 16], | ||
[5, 17], | ||
[5, 22], | ||
[5, 24], | ||
[6, 10], | ||
[6, 16], | ||
[6, 24], | ||
[7, 11], | ||
[7, 15], | ||
[8, 12], | ||
[8, 13] | ||
] | ||
|
||
["Bunnies"] | ||
alive_cells = [ | ||
[10, 10], | ||
[10, 16], | ||
[11, 12], | ||
[11, 16], | ||
[12, 12], | ||
[12, 15], | ||
[12, 17], | ||
[13, 11], | ||
[13, 13] | ||
] |
Oops, something went wrong.