From 190afd4669c7c4fc3cfe82b7825913b07f27643b Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:56:41 +0000 Subject: [PATCH 1/6] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index fdcf0ceedf1f..e6a1ff356143 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -585,6 +585,7 @@ * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) * [Hexagonal Number](maths/hexagonal_number.py) * [Integration By Simpson Approx](maths/integration_by_simpson_approx.py) + * [Interquartile Range](maths/interquartile_range.py) * [Is Int Palindrome](maths/is_int_palindrome.py) * [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py) * [Is Square Free](maths/is_square_free.py) From 1ba195a7fd7deb1fbe7dafdf7233cb39630736fd Mon Sep 17 00:00:00 2001 From: CaedenPH Date: Wed, 16 Aug 2023 14:27:18 +0100 Subject: [PATCH 2/6] feat(cellular_automata): Langonts ant algorithm --- cellular_automata/langtons_ant.py | 110 ++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 cellular_automata/langtons_ant.py diff --git a/cellular_automata/langtons_ant.py b/cellular_automata/langtons_ant.py new file mode 100644 index 000000000000..0388e2b8f88b --- /dev/null +++ b/cellular_automata/langtons_ant.py @@ -0,0 +1,110 @@ +""" +Langton's ant + +@ https://en.wikipedia.org/wiki/Langton%27s_ant +@ https://upload.wikimedia.org/wikipedia/commons/0/09/LangtonsAntAnimated.gif +""" + +from functools import partial + +from matplotlib import pyplot as plt +from matplotlib.animation import FuncAnimation + +WIDTH = 80 +HEIGHT = 80 + + +class LangtonsAnt: + """ + Represents the main LangonsAnt algorithm. + + >>> la = LangtonsAnt(2, 2) + >>> la.board + [[True, True], [True, True]] + >>> la.ant_position + (1, 1) + """ + + def __init__(self, width: int, height: int) -> None: + # Each square is either True or False where True is white and False is black + self.board = [[True] * width for _ in range(height)] + self.ant_position: tuple[int, int] = (width // 2, height // 2) + + # Initially pointing left (similar to the the wikipedia image) + # (0 = 0° | 1 = 90° | 2 = 180 ° | 3 = 270°) + self.ant_direction: int = 3 + + def move_ant(self, axes: plt.Axes | None, display: bool, _frame: int) -> None: + """ + Performs three tasks: + 1. The ant turns either clockwise or anti-clockwise according to the colour + of the square that it is currently on. If the square is white, the ant + turns clockwise, and if the square is black the ant turns anti-clockwise + 2. The ant moves one square in the direction that it is currently facing + 3. The square the ant was previously on is inverted (White -> Black and + Black -> White) + + If display is True, the board will also be displayed on the axes + + >>> la = LangtonsAnt(2, 2) + >>> la.move_ant(None, True, 0) + >>> la.board + [[True, True], [True, False]] + >>> la.move_ant(None, True, 0) + >>> la.board + [[True, False], [True, False]] + """ + directions = { + 0: [-1, 0], # 0° + 1: [0, 1], # 90° + 2: [1, 0], # 180° + 3: [0, -1], # 270° + } + x, y = self.ant_position + + # Turn clockwise or anti-clockwise according to colour of square + if self.board[x][y] is True: + # The square is white so turn 90° clockwise + self.ant_direction = ( + 0 if self.ant_direction == 3 else self.ant_direction + 1 + ) + else: + # The square is black so turn 90° anti-clockwise + self.ant_direction = ( + 3 if self.ant_direction == 0 else self.ant_direction - 1 + ) + + # Move ant + move_x, move_y = directions[self.ant_direction] + self.ant_position = (x + move_x, y + move_y) + + # Flip colour of square + self.board[x][y] = not self.board[x][y] + + if display and axes: + # Display the board on the axes + axes.get_xaxis().set_ticks([]) + axes.get_yaxis().set_ticks([]) + axes.imshow(self.board, cmap="gray", interpolation="nearest") + + def display(self, frames: int = 100_000) -> None: + """ + Displays the board without delay in a matplotlib plot + to visually understand and track the ant. + + >>> _ = LangtonsAnt(WIDTH, HEIGHT) + """ + fig, ax = plt.subplots() + # Assign animation to a variable to prevent it from getting garbage collected + self.animation = FuncAnimation( + fig, partial(self.move_ant, ax, True), frames=frames, interval=0 + ) + plt.show() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + LangtonsAnt(WIDTH, HEIGHT).display() From b01927cf8f400852fb9161c2402af68f14c724e0 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:27:52 +0000 Subject: [PATCH 3/6] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 8d1567465fbc..e075a3b520ac 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -72,6 +72,7 @@ ## Cellular Automata * [Conways Game Of Life](cellular_automata/conways_game_of_life.py) * [Game Of Life](cellular_automata/game_of_life.py) + * [Langtons Ant](cellular_automata/langtons_ant.py) * [Nagel Schrekenberg](cellular_automata/nagel_schrekenberg.py) * [One Dimensional](cellular_automata/one_dimensional.py) * [Wa Tor](cellular_automata/wa_tor.py) From 0506930412d210e2679948cc48e2d4d9bfabf818 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Mon, 21 Aug 2023 15:11:50 +0100 Subject: [PATCH 4/6] Update cellular_automata/langtons_ant.py Co-authored-by: Tianyi Zheng --- cellular_automata/langtons_ant.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cellular_automata/langtons_ant.py b/cellular_automata/langtons_ant.py index 0388e2b8f88b..c9dce02e14d6 100644 --- a/cellular_automata/langtons_ant.py +++ b/cellular_automata/langtons_ant.py @@ -65,9 +65,7 @@ def move_ant(self, axes: plt.Axes | None, display: bool, _frame: int) -> None: # Turn clockwise or anti-clockwise according to colour of square if self.board[x][y] is True: # The square is white so turn 90° clockwise - self.ant_direction = ( - 0 if self.ant_direction == 3 else self.ant_direction + 1 - ) + self.ant_direction = (self.ant_direction + 1) % 4 else: # The square is black so turn 90° anti-clockwise self.ant_direction = ( From 3789e5e1a8226ce1bcd27d3976c785c37d5f424b Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Mon, 21 Aug 2023 15:13:05 +0100 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Tianyi Zheng --- cellular_automata/langtons_ant.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/cellular_automata/langtons_ant.py b/cellular_automata/langtons_ant.py index c9dce02e14d6..43de049db720 100644 --- a/cellular_automata/langtons_ant.py +++ b/cellular_automata/langtons_ant.py @@ -55,10 +55,10 @@ def move_ant(self, axes: plt.Axes | None, display: bool, _frame: int) -> None: [[True, False], [True, False]] """ directions = { - 0: [-1, 0], # 0° - 1: [0, 1], # 90° - 2: [1, 0], # 180° - 3: [0, -1], # 270° + 0: (-1, 0), # 0° + 1: (0, 1), # 90° + 2: (1, 0), # 180° + 3: (0, -1), # 270° } x, y = self.ant_position @@ -68,9 +68,7 @@ def move_ant(self, axes: plt.Axes | None, display: bool, _frame: int) -> None: self.ant_direction = (self.ant_direction + 1) % 4 else: # The square is black so turn 90° anti-clockwise - self.ant_direction = ( - 3 if self.ant_direction == 0 else self.ant_direction - 1 - ) + self.ant_direction = (self.ant_direction - 1) % 4 # Move ant move_x, move_y = directions[self.ant_direction] From 2e77104efcd5e7f343b0b5a768f63795f8203399 Mon Sep 17 00:00:00 2001 From: caedenph Date: Mon, 21 Aug 2023 18:43:48 +0100 Subject: [PATCH 6/6] fix(langtons-ant): Set funcanimation interval to 1 --- cellular_automata/langtons_ant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cellular_automata/langtons_ant.py b/cellular_automata/langtons_ant.py index 0388e2b8f88b..150621d10a26 100644 --- a/cellular_automata/langtons_ant.py +++ b/cellular_automata/langtons_ant.py @@ -97,7 +97,7 @@ def display(self, frames: int = 100_000) -> None: fig, ax = plt.subplots() # Assign animation to a variable to prevent it from getting garbage collected self.animation = FuncAnimation( - fig, partial(self.move_ant, ax, True), frames=frames, interval=0 + fig, partial(self.move_ant, ax, True), frames=frames, interval=1 ) plt.show()