|
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