|
15 | 15 | """ |
16 | 16 |
|
17 | 17 | import doctest |
18 | | -import sys |
19 | 18 |
|
20 | 19 | import matplotlib.pyplot as plt |
21 | 20 | import numpy as np |
22 | 21 | from matplotlib import animation |
23 | 22 | from matplotlib.colors import ListedColormap |
| 23 | +from matplotlib.image import AxesImage |
24 | 24 |
|
25 | 25 |
|
26 | 26 | def create_random_grid( |
@@ -407,7 +407,7 @@ def visualize_cellular_automaton( |
407 | 407 | cbar.set_ticklabels(["Dead"] + [f"Age {i}" for i in range(1, max_age + 1)]) |
408 | 408 |
|
409 | 409 | # Animation function |
410 | | - def animate(frame: int) -> None: |
| 410 | + def animate(frame: int) -> list[AxesImage]: |
411 | 411 | """ |
412 | 412 | Animation update function for matplotlib FuncAnimation. |
413 | 413 |
|
@@ -586,192 +586,7 @@ def run_interactive_simulation( |
586 | 586 | return anim |
587 | 587 |
|
588 | 588 |
|
589 | | -# ------------------------------------------------------------------------- |
590 | | -# Helper demo functions |
591 | | -# ------------------------------------------------------------------------- |
592 | | - |
593 | | - |
594 | | -def demo_game_of_life(size: int = 50, steps: int = 100): |
595 | | - """Demo using Game of Life rules on a Von Neumann grid.""" |
596 | | - initial_state = np.random.Generator(0, 2, size=(size, size)) |
597 | | - history = simulate_von_neumann_cellular_automaton( |
598 | | - initial_state, generations=steps, |
599 | | - birth_rules={3}, survival_rules={2, 3} |
600 | | - ) |
601 | | - visualize_cellular_automaton(history) |
602 | | - |
603 | | - |
604 | | -def demo_highlife(size: int = 50, steps: int = 100): |
605 | | - """Demo using HighLife rules (B36/S23).""" |
606 | | - initial_state = np.random.Generator(0, 2, size=(size, size)) |
607 | | - history = simulate_von_neumann_cellular_automaton( |
608 | | - initial_state, generations=steps, |
609 | | - birth_rules={3, 6}, survival_rules={2, 3} |
610 | | - ) |
611 | | - visualize_cellular_automaton(history) |
612 | | - |
613 | | - |
614 | | -def demo_oscillator(steps: int = 20): |
615 | | - """Demo with a simple 3-cell Von Neumann oscillator pattern.""" |
616 | | - size = 10 |
617 | | - initial_state = np.zeros((size, size), dtype=int) |
618 | | - |
619 | | - # A vertical 3-cell oscillator in the center |
620 | | - center = size // 2 |
621 | | - initial_state[center - 1, center] = 1 |
622 | | - initial_state[center, center] = 1 |
623 | | - initial_state[center + 1, center] = 1 |
624 | | - |
625 | | - history = simulate_von_neumann_cellular_automaton( |
626 | | - initial_state, |
627 | | - generations=steps, |
628 | | - birth_rules={3}, # Standard Game of Life birth rule |
629 | | - survival_rules={2, 3} # Standard Game of Life survival rule |
630 | | - ) |
631 | | - visualize_cellular_automaton(history) |
632 | | - |
633 | | - |
634 | | -def demo_random_rules(size: int = 50, steps: int = 100): |
635 | | - """Demo with random birth/survival rules.""" |
636 | | - birth_rules = set( |
637 | | - np.random.Generator(range(5), |
638 | | - size=np.random.Generator(1, 5), |
639 | | - replace=False) |
640 | | - ) |
641 | | - survival_rules = set( |
642 | | - np.random.Generator(range(5), |
643 | | - size=np.random.Generator(1, 5), |
644 | | - replace=False) |
645 | | - ) |
646 | | - initial_state = np.random.Generator(0, 2, size=(size, size)) |
647 | | - history = simulate_von_neumann_cellular_automaton( |
648 | | - initial_state, generations=steps, |
649 | | - birth_rules=birth_rules, survival_rules=survival_rules |
650 | | - ) |
651 | | - visualize_cellular_automaton(history) |
652 | | - |
653 | | - |
654 | | -def demo_statistics(size: int = 50, steps: int = 100): |
655 | | - """Demo that plots live cell counts over time.""" |
656 | | - initial_state = np.random.Generator(0, 2, size=(size, size)) |
657 | | - history = simulate_von_neumann_cellular_automaton(initial_state, generations=steps) |
658 | | - |
659 | | - # collect statistics |
660 | | - live_counts = [np.sum(state > 0) for state in history] |
661 | | - plt.figure(figsize=(6, 4)) |
662 | | - plt.plot(range(steps + 1), live_counts, label='Live Cells') |
663 | | - plt.xlabel("Generation") |
664 | | - plt.ylabel("Number of live cells") |
665 | | - plt.title("Cell Count Over Time") |
666 | | - plt.legend() |
667 | | - plt.show() |
668 | | - |
669 | | - |
670 | | -# ------------------------------------------------------------------------- |
671 | | -# Main demo orchestrator |
672 | | -# ------------------------------------------------------------------------- |
673 | | - |
674 | | - |
675 | | -def demonstrate_cellular_automaton_features() -> None: |
676 | | - """ |
677 | | - Run all demonstration functions sequentially. |
678 | | -
|
679 | | - This will open multiple matplotlib animation windows and print statistics. |
680 | | -
|
681 | | - Examples |
682 | | - -------- |
683 | | - >>> demonstrate_cellular_automaton_features() # doctest: +SKIP |
684 | | - """ |
685 | | - print("=" * 80) |
686 | | - print("VON NEUMANN CELLULAR AUTOMATON - FEATURE DEMONSTRATION") |
687 | | - print("=" * 80) |
688 | | - |
689 | | - demo_game_of_life() |
690 | | - demo_highlife() |
691 | | - demo_oscillator() |
692 | | - demo_random_rules() |
693 | | - demo_statistics() |
694 | | - |
695 | | - print("=" * 80) |
696 | | - print("Demonstration complete.") |
697 | | - print("=" * 80) |
698 | | - |
699 | | - |
700 | | -def quick_demo(rule_name: str = "conway") -> None: |
701 | | - """ |
702 | | - Quick demonstration function for specific rule sets. |
703 | | -
|
704 | | - Args: |
705 | | - rule_name: One of 'conway', 'highlife', 'seeds', 'stable' |
706 | | -
|
707 | | - Examples: |
708 | | - >>> quick_demo("conway") # doctest: +SKIP |
709 | | - >>> quick_demo("seeds") # doctest: +SKIP |
710 | | - """ |
711 | | - rule_configs = { |
712 | | - "conway": { |
713 | | - "birth_rules": {3}, |
714 | | - "survival_rules": {2, 3}, |
715 | | - "title": "Conway-like Rules (B3/S23)", |
716 | | - "generations": 50, |
717 | | - }, |
718 | | - "highlife": { |
719 | | - "birth_rules": {3, 6}, |
720 | | - "survival_rules": {2, 3}, |
721 | | - "title": "High-Life Rules (B36/S23)", |
722 | | - "generations": 60, |
723 | | - }, |
724 | | - "seeds": { |
725 | | - "birth_rules": {2}, |
726 | | - "survival_rules": set(), |
727 | | - "title": "Seeds Rules (B2/S)", |
728 | | - "generations": 25, |
729 | | - }, |
730 | | - "stable": { |
731 | | - "birth_rules": {2, 4}, |
732 | | - "survival_rules": {1, 2, 3, 4}, |
733 | | - "title": "Stable Rules (B24/S1234)", |
734 | | - "generations": 40, |
735 | | - }, |
736 | | - } |
737 | | - |
738 | | - if rule_name not in rule_configs: |
739 | | - print(f"Unknown rule set: {rule_name}") |
740 | | - print(f"Available: {', '.join(rule_configs.keys())}") |
741 | | - return |
742 | | - |
743 | | - config = rule_configs[rule_name] |
744 | | - print(f"Running quick demo: {config['title']}") |
745 | | - |
746 | | - try: |
747 | | - run_interactive_simulation( |
748 | | - grid_rows=25, |
749 | | - grid_columns=35, |
750 | | - generations=config["generations"], |
751 | | - birth_rules=config["birth_rules"], |
752 | | - survival_rules=config["survival_rules"], |
753 | | - max_age=5, |
754 | | - animation_speed=150, |
755 | | - show_static=True, |
756 | | - ) |
757 | | - except (ValueError, RuntimeError) as e: |
758 | | - print(f"Error running demo: {e}") |
759 | | - |
760 | | - |
761 | 589 | if __name__ == "__main__": |
762 | 590 | # Run doctests |
763 | 591 | print("Running doctests...") |
764 | 592 | doctest.testmod(verbose=True) |
765 | | - |
766 | | - # Check if this is being run interactively or with specific demo request |
767 | | - if len(sys.argv) > 1: |
768 | | - # Command line usage: python von_neumann.py demo |
769 | | - if sys.argv[1] == "demo": |
770 | | - demonstrate_cellular_automaton_features() |
771 | | - elif sys.argv[1] in ["conway", "highlife", "seeds", "stable"]: |
772 | | - quick_demo(sys.argv[1]) |
773 | | - else: |
774 | | - print("Usage: python von_neumann.py [demo|conway|highlife|seeds|stable]") |
775 | | - else: |
776 | | - # Default interactive demonstration |
777 | | - demonstrate_cellular_automaton_features() |
0 commit comments