diff --git a/.gitignore b/.gitignore
index b6e4761..5e36f5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -127,3 +127,6 @@ dmypy.json
# Pyre type checker
.pyre/
+
+# image results
+*.png
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..73f69e0
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..11a5d8e
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+main.py
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..f02d5e7
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..49a9249
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/random_walk.iml b/.idea/random_walk.iml
new file mode 100644
index 0000000..d0876a7
--- /dev/null
+++ b/.idea/random_walk.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index a53d95c..458adac 100644
--- a/README.md
+++ b/README.md
@@ -1,55 +1,71 @@
# Random Walk Simulation
-This is a **group exercise**, so you should be working in pairs of two students. It's **30% of your final grade**.
+## Write an extended random walk program
-The Goal is to **practise writing readable, maintainable and reliable code collaboratively.**
+In this repo you find a basic implementation of a [random walk simulation](https://en.wikipedia.org/wiki/Random_walk) in 2-dimensional space taken from [this blogpost](https://www.geeksforgeeks.org/random-walk-implementation-python/). Running the code yields an image which shows the path of the random walk.
-## Group Exercise
+The result will be directly shown to the user and additionally saved as png.
-1. One student of your group forks the code from [https://github.com/advanced-geoscripting-2021/random_walker.git](https://github.com/advanced-geoscripting-2021/random_walker.git)
+
+start: point; end: cross
-2. This student invites the other student as a collaborator to the forked repository. Now you can both work on the code.
+An example for the landscape raster walker
+
-3. Adapt the code to fulfil the requirements (see below).
+## Setting up the project
+1. clone repository
+2. setup a virtual environment
+3. activate the virtual environment
+4. install required libs and packages locally (see requirements.txt)
-4. Code review: Each group reviews the code of another group.
+#### For the advanced geoscripting course:
+- activate the corresponding virtual environment
+- run ``conda install -c conda-forge click``
+- run ``pip install --editable .``
-5. Improve your code based on the review you got.
+## How to run the tool
+````
+$ random_walker
-## Write an extended random walk program
-
-In this repo you find a basic implementation of a [random walk simulation](https://en.wikipedia.org/wiki/Random_walk) in 2-dimensional space taken from [this blogpost](https://www.geeksforgeeks.org/random-walk-implementation-python/). Running the code yields an image which shows the path of the random walk.
+Usage: random_walker [OPTIONS] COMMAND [ARGS]...
-
+Options:
+ --verbose TEXT Will print verbose messages.
+ --help Show this message and exit.
-The program works but it is not very readable. In addition, you should **extend the program based on the requirements listed below.
+Commands:
+ run execute command to generate random walkers
+ ````
-**Remember to apply the best practices in scientific computing** to make the code more readable, maintainable, reusable and efficient.
+### How to use the run method
+````
+Usage: random_walker run [OPTIONS]
-### Minimum requirements:
+ execute command to generate random walkers
-Extend the program so the following requirements are met:
+Options:
+ -ts, --total_steps INTEGER Specify the number of total steps for each
+ random walker, Default is 10,000, Minimum 100
-1. The program should be able to simulate multiple random walkers.
-2. The program should be executable from the command line.
-3. The user should be able to specify the number of random walkers through a command line parameter.
-4. Document the dependencies and instructions of how to run the program in your README.md.
+ -tw, --total_walkers INTEGER Specify the number of total walkers, Default
+ is 1, Minimum is 1
-### Additional requirements:
+ -ss, --step_size INTEGER Specify the size of the steps taken, Default
+ is 1
-1. Create three different types of walkers, e.g. a "fast walker" which has a bigger step size.
-2. Add a "landscape" in which the random walkers are walking in which contains obstacles which the walkers cannot cross (e.g. a lake)
-3. Invent and implement another functionality of your own.
+ -l, --landscape BOOLEAN Specify whether a grid landscape exists as
+ base layer or not, Default is False
-Be creative here! :)
+ -sp, --start_point BOOLEAN Specify whether the walkers shall start from
+ the same point or not, Default is False
-## Code Review
+ -mp, --mov_pattern BOOLEAN Specify the neighborhood movement pattern,
+ False is Neumann, True is Moor
-Review the code of another group: (tuesday afternoon or wednesday morning)
+ --help Show this message and exit.
-1. Does it work properly? Try to make it fail!
-2. Are the best-practices implemented in the code?
-3. Is the documentation clear?
-4. Can you adapt the code easily? E.g. try to create a new type of random walker which moves two cells per iteration.
+````
+## Example
+``random_walker run --total_steps 10000 --step_size 2 -l False``
diff --git a/main.py b/main.py
index 9aac7f0..7ac0ba9 100644
--- a/main.py
+++ b/main.py
@@ -4,37 +4,147 @@
# Python code for 2D random walk.
# Source: https://www.geeksforgeeks.org/random-walk-implementation-python/
-import numpy
-import matplotlib.pyplot as plt
-import random
-
-# defining the number of steps
-n = 100000
-
-# creating two array for containing x and y coordinate
-# of size equals to the number of size and filled up with 0's
-x = numpy.zeros(n)
-y = numpy.zeros(n)
-
-# filling the coordinates with random variables
-for i in range(1, n):
- val = random.randint(1, 4)
- if val == 1:
- x[i] = x[i - 1] + 1
- y[i] = y[i - 1]
- elif val == 2:
- x[i] = x[i - 1] - 1
- y[i] = y[i - 1]
- elif val == 3:
- x[i] = x[i - 1]
- y[i] = y[i - 1] + 1
+
+import click
+import raster_walker as rw
+import vector_walker as vw
+
+
+# user input – click
+_total_steps_option = [
+ click.option(
+ "--total_steps",
+ "-ts",
+ default=10000,
+ type=int,
+ help="Specify the number of total steps for each random walker,"
+ "Default is 10,000, Minimum 100",
+ )
+]
+
+_total_walkers_option = [
+ click.option(
+ "--total_walkers",
+ "-tw",
+ default=1,
+ type=int,
+ help="Specify the number of total walkers, Default is 1, Minimum is 1",
+ )
+]
+
+_step_size_option = [
+ click.option(
+ "--step_size",
+ "-ss",
+ default=1,
+ type=int,
+ help="Specify the size of the steps taken, Default is 1",
+ )
+]
+
+_landscape_option = [
+ click.option(
+ "--landscape",
+ "-l",
+ default=False,
+ type=bool,
+ help="Specify whether a grid landscape exists as base layer or not, Default is False",
+ )
+]
+
+_start_point_option = [
+ click.option(
+ "--start_point",
+ "-sp",
+ default=False,
+ type=bool,
+ help="Specify whether the walkers shall start from the same point or not, Default is False",
+ )
+]
+
+_mov_pattern_option = [
+ click.option(
+ "--mov_pattern",
+ "-mp",
+ default=False,
+ type=bool,
+ help="Specify the neighborhood movement pattern, False is Neumann, True is Moor",
+ )
+]
+
+
+def add_options(options):
+ """Functions adds options to cli."""
+
+ def _add_options(func):
+ for option in reversed(options):
+ func = option(func)
+ return func
+ return _add_options
+
+
+@click.group()
+@click.option('--verbose', '-v', is_flag=False, help="Will print verbose messages.")
+def cli(verbose: bool) -> None:
+ if verbose:
+ click.echo("We are in the verbose mode. Which does not make any"
+ "difference right now.. but hey, have fun!")
+
+
+@cli.command()
+@add_options(_total_steps_option)
+@add_options(_total_walkers_option)
+@add_options(_step_size_option)
+@add_options(_landscape_option)
+@add_options(_start_point_option)
+@add_options(_mov_pattern_option)
+def run(
+ total_steps: int,
+ total_walkers: int,
+ step_size: int,
+ landscape: bool,
+ start_point: bool,
+ mov_pattern: bool
+) -> None:
+ """ execute command to generate random walkers """
+ run_random_walkers(total_steps, total_walkers, step_size, landscape, start_point, mov_pattern)
+
+
+def run_random_walkers(total_steps, total_walkers, step_size, landscape, diff_start, mov_pattern):
+ """
+ executes the random walker tool based on input data
+ :param total_steps: number of total steps of the random walker
+ :param total_walkers: number of walkers
+ :param step_size: step size for each step
+ :param landscape: boolean, if True, 2D area with obstacles is generated as base layer
+ :param diff_start: boolean, if True, different start points for each walker
+ :param mov_pattern: boolean, if True, Moor'sche neighboorhood is used, else Neumann
+ :return:
+ """
+
+ # adjust wrong input
+ total_steps = max(total_steps, 100)
+ total_walkers = max(total_walkers, 1)
+
+ # diverted because of the completely different implementation methods ->
+ # could be done better in the future
+ if landscape:
+ # percentage of how much space obstacles shall block
+ fill_percentage = 0.1
+ landscape_raster = rw.create_raster(total_steps, fill_percentage)
+ walk = rw.r_walker(total_steps, landscape_raster)
+ # plot landscape raster with obstacles and walker
+ rw.plot_raster(walk, total_steps)
else:
- x[i] = x[i - 1]
- y[i] = y[i - 1] - 1
+ # creating two arrays for containing x and y coordinate
+ # of size equals to the number of size and filled up with 0's
+ x_arr, y_arr = vw.create_walking_space(total_steps)
+
+ # multiple walkers
+ list_x, list_y = vw.multiple_v_walkers(x_arr, y_arr, total_steps, total_walkers,
+ step_size, diff_start, mov_pattern)
+ vw.plot_v_walkers(total_steps, total_walkers, list_x, list_y)
-# plotting the walk
-plt.title("Random Walk ($n = " + str(n) + "$ steps)")
-plt.plot(x, y)
-plt.savefig("./rand_walk_{}.png".format(n))
-plt.show()
\ No newline at end of file
+if __name__ == "__main__":
+ run_random_walkers(100, 5, 1, True, True, True)
diff --git a/rand_walk_100000.png b/rand_walk_100000.png
deleted file mode 100644
index 5582dd8..0000000
Binary files a/rand_walk_100000.png and /dev/null differ
diff --git a/rand_walk_5_100.png b/rand_walk_5_100.png
new file mode 100644
index 0000000..005f474
Binary files /dev/null and b/rand_walk_5_100.png differ
diff --git a/raster_walker.py b/raster_walker.py
new file mode 100644
index 0000000..b89a9d1
--- /dev/null
+++ b/raster_walker.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+""" Rasterized Walker"""
+
+
+import random
+import math
+import numpy as np
+import matplotlib.pyplot as plt
+from matplotlib.colors import ListedColormap
+from matplotlib.patches import Patch
+
+
+def check_landscape(landscape, position):
+ """
+ Checks if next position of walker intersects given landscape
+ :param landscape:
+ :param position:
+ :return:
+ """
+ # implement endless border -> reach border on the right, come in on the left
+ # like the good old snake
+ if abs(position[0]) >= landscape.shape[0]:
+ position[0] = position[0] % landscape.shape[0]
+ if abs(position[1]) >= landscape.shape[1]:
+ position[1] = position[1] % landscape.shape[1]
+
+ # check if the given position is an obstacle or the starting point
+ if landscape[position[0], position[1]] != 1 and landscape[position[0], position[1]] != 3:
+ return True
+ return False
+
+
+def raster_one_step(direction, curr_pos, future_pos):
+ """
+ Alters given position based on given direction
+ :param direction: String, celestial direction
+ :param curr_pos: the current position
+ :param future_pos: future position
+ :return: updated future position
+ """
+ if direction == "EAST":
+ future_pos[1] = curr_pos[1] + 1
+ elif direction == "WEST":
+ future_pos[1] = curr_pos[1] - 1
+ elif direction == "NORTH":
+ future_pos[0] = curr_pos[0] + 1
+ else:
+ future_pos[0] = curr_pos[0] - 1
+
+ return future_pos
+
+
+def r_walker(total_steps, landscape, direction_set=("NORTH", "SOUTH", "EAST", "WEST")):
+ """
+ Random walker on 2D array with an obstacle with step size 1
+ :param total_steps: integer specifying the number of steps by the walker
+ :param landscape: 2D numpy array with some landscape features
+ :param direction_set: defines a set of directions, default values North, South, East, West
+ :return: x, y numpy arrays
+ """
+
+ # start upper left corner
+ curr_pos = [0, 0]
+ future_pos = [0, 0]
+
+ # give the starting position another value for plotting it later
+ landscape[curr_pos[0], curr_pos[1]] = 3
+
+ for step in range(0, total_steps):
+
+ check = False
+
+ while not check:
+ direction = random.choice(direction_set)
+ future_pos = raster_one_step(direction, curr_pos, future_pos)
+ check = check_landscape(landscape, future_pos)
+
+ landscape[future_pos[0], future_pos[1]] = 2
+ curr_pos = future_pos
+ return landscape
+
+
+def create_raster(total_steps: int, fill=0.1):
+ """
+ generates x,y 1D arrays with zeros for total_steps or
+ generates landscape 2D array with obstacles if landscape is true
+ :param total_steps: total count of steps
+ :param fill: how much of the area of the 2D array shall be filled with obstacles (percentage)
+ :return:
+ """
+
+ # side_length as square root of the total steps
+ side_length = int(math.sqrt(total_steps))
+
+ # create 2D array
+ arr = np.zeros((side_length, side_length))
+
+ total_area = arr.size
+ fill_area = int(total_area * fill)
+ if fill_area % 2 == 1:
+ fill_area += 1
+
+ # get length of one size of the square
+ side_length_obstacle = int(math.sqrt(fill_area))
+
+ centre = side_length / 2
+
+ # centre with x, y
+ upper_left = (int(centre - side_length_obstacle / 2), int(centre - side_length_obstacle / 2))
+
+ # places an obstacle as square in the middle of the landscape arr
+ for row in range(side_length_obstacle):
+ for col in range(side_length_obstacle):
+ arr[upper_left[0] + col, upper_left[1] + row] = 1
+ return arr
+
+
+def plot_raster(arr, total_steps):
+ """
+ Plots landscape with the path of the random walker
+ :param total_steps: total number of steps
+ :param arr: raster array of landscape
+ :return:
+ """
+ cmap = ListedColormap(["grey", "black", "darkgreen", "red"])
+
+ fig, axs = plt.subplots(figsize=(10, 5))
+
+ axs.imshow(arr, cmap=cmap)
+
+ axs.set_title("Random Walk (Number of walkers = 1, total steps: " + str(total_steps))
+
+ # Add a legend for labels
+ legend_labels = {"black": "obstacle", "darkgreen": "path", "red": "start point"}
+
+ patches = [Patch(color=color, label=label)
+ for color, label in legend_labels.items()]
+
+ axs.legend(handles=patches,
+ bbox_to_anchor=(1.35, 1),
+ facecolor="white")
+
+ axs.set_axis_off()
+ plt.savefig(".\\rand_walk_raster_{}.png".format(total_steps))
+ plt.show()
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..4f3d2cd
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+matplotlib~=3.3.4
+numpy~=1.20.2
+click~=7.1.2
+setuptools~=52.0.0
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..f1c8b79
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+setup(
+ name="random_walker",
+ version="0.1",
+ py_modules=["main"],
+ install_requires=[
+ "Click",
+ ],
+ entry_points='''
+ [console_scripts]
+ random_walker=main:cli
+ '''
+)
\ No newline at end of file
diff --git a/test_raster_walkers.py b/test_raster_walkers.py
new file mode 100644
index 0000000..5e35812
--- /dev/null
+++ b/test_raster_walkers.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+""" tests the raster_walker.py functionality"""
+
+
+import numpy as np
+import raster_walker as rw
+
+
+def test_create_raster():
+ """ Checks if raster creation works and implements the obstacle correctly """
+ created_raster = rw.create_raster(100)
+ expected_raster = np.zeros((10, 10))
+ expected_raster[3:6, 3:6] = 1
+ assert np.array_equal(created_raster, expected_raster)
+
+
+def test_raster_one_step():
+ """ Checks if raster one step returns the correct changes """
+ direction_set = ("NORTH", "SOUTH", "EAST", "WEST")
+ curr_pos = [0, 0]
+ future_pos = [0, 0]
+
+ pos_north = rw.raster_one_step(direction_set[0], curr_pos, future_pos)
+ future_pos = [0, 0]
+ pos_south = rw.raster_one_step(direction_set[1], curr_pos, future_pos)
+ future_pos = [0, 0]
+ pos_east = rw.raster_one_step(direction_set[2], curr_pos, future_pos)
+ future_pos = [0, 0]
+ pos_west = rw.raster_one_step(direction_set[3], curr_pos, future_pos)
+
+ expected_north = [1, 0]
+ expected_south = [-1, 0]
+ expected_east = [0, 1]
+ expected_west = [0, -1]
+
+ assert pos_north == expected_north
+ assert pos_south == expected_south
+ assert pos_east == expected_east
+ assert pos_west == expected_west
+
+
+def test_check_landscape():
+ """ Checks if raster obstacle check is working correct """
+
+ raster = np.zeros((4, 4))
+ raster[1:3, 1:3] = 1
+ raster[3, 3] = 3
+ position1 = [0, 0]
+ position2 = [2, 2]
+ position3 = [0, -1]
+ position4 = [-1, 0]
+ position5 = [4, 0]
+ position6 = [0, 4]
+ position7 = [3, 3]
+
+ assert rw.check_landscape(raster, position1) is True
+ assert rw.check_landscape(raster, position2) is False
+ assert rw.check_landscape(raster, position3) is True
+ assert rw.check_landscape(raster, position4) is True
+ assert rw.check_landscape(raster, position5) is True
+ assert rw.check_landscape(raster, position6) is True
+ assert rw.check_landscape(raster, position7) is False
+
+
+if __name__ == "__main__":
+ test_raster_one_step()
+ test_create_raster()
+ test_check_landscape()
diff --git a/test_vector_walker.py b/test_vector_walker.py
new file mode 100644
index 0000000..3eccc4e
--- /dev/null
+++ b/test_vector_walker.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Unit testing of walkers"""
+
+import raster_walker as rw
+import vector_walker as vw
+
+
+def test_walker_next():
+ # define initial position and direction
+ start = (2, 2)
+ total_steps = 1
+ x, y = vw.create_walking_space(total_steps)
+ direction = "EAST"
+ step_size = 1
+ expected_new_x = 3
+ expected_new_y = 2
+
+ # execute the nextstep function
+ new_x, new_y = vw.next_step(x, y, 0, direction, step_size)
+
+
+ # assert
+ assert new_x[0] + start[0] == expected_new_x
+ assert new_y[0] + start[1] == expected_new_y
diff --git a/vector_walker.py b/vector_walker.py
new file mode 100644
index 0000000..8e0d54b
--- /dev/null
+++ b/vector_walker.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Vectorized Walker"""
+
+import random
+import math as m
+import matplotlib.pyplot as plt
+import numpy as np
+
+
+def next_step(x_arr, y_arr, pos, direction, step_size):
+ """
+ Returns the next step for a random walker
+ :param x_arr: numpy array of the walker's path – x-coordinates
+ :param y_arr: numpy array of the walker's path – y-coordinates
+ :param pos: which step is walker doing
+ :param direction: direction of the step
+ :param step_size: size of the step
+ :return: updated arrays of the path
+ """
+ # go east
+ if direction == "EAST":
+ x_arr[pos] = x_arr[pos - 1] + step_size
+ y_arr[pos] = y_arr[pos - 1]
+ # go west
+ elif direction == "WEST":
+ x_arr[pos] = x_arr[pos - 1] - step_size
+ y_arr[pos] = y_arr[pos - 1]
+ # go north
+ elif direction == "NORTH":
+ x_arr[pos] = x_arr[pos - 1]
+ y_arr[pos] = y_arr[pos - 1] + step_size
+ # go south
+ elif direction == "SOUTH":
+ x_arr[pos] = x_arr[pos - 1]
+ y_arr[pos] = y_arr[pos - 1] - step_size
+ # go northeast
+ elif direction == "NORTHEAST":
+ x_arr[pos] = x_arr[pos - 1] + step_size
+ y_arr[pos] = y_arr[pos - 1] + step_size
+ # go northwest
+ elif direction == "NORTHWEST":
+ x_arr[pos] = x_arr[pos - 1] - step_size
+ y_arr[pos] = y_arr[pos - 1] + step_size
+ # go southeast
+ elif direction == "SOUTHEAST":
+ x_arr[pos] = x_arr[pos - 1] + step_size
+ y_arr[pos] = y_arr[pos - 1] - step_size
+ # go southwest
+ elif direction == "SOUTHWEST":
+ x_arr[pos] = x_arr[pos - 1] - step_size
+ y_arr[pos] = y_arr[pos - 1] - step_size
+ return x_arr, y_arr
+
+
+def different_start_pos(total_steps):
+ """
+ Returns a random starting position of a walker
+ :param total_steps: based on number of steps the start shift is chosen
+ :return: tuple of random integer coordinates
+ """
+ if total_steps <= 10:
+ start_shift = 10
+ else:
+ start_shift = int(m.sqrt(total_steps))
+ return random.randint(-start_shift, start_shift), random.randint(-start_shift, start_shift)
+
+
+def get_random_direction(direction_set):
+ """
+ Returns a random direction
+ :param direction_set: von Neumann or Moor'sche neighborhood
+ :return: random direction (f.e. 'NORTH')
+ """
+ return random.choice(direction_set)
+
+
+def create_walking_space(total_steps):
+ """
+ Creating empty arrays for x,y-coordinates
+ :param total_steps: number of steps
+ :return: numpy arrays
+ """
+ # for random walker without landscape
+ x_arr, y_arr = np.zeros(total_steps).astype(int), np.zeros(total_steps).astype(int)
+ return x_arr, y_arr
+
+
+def v_walker(x_arr, y_arr, total_steps, diff_start, step_size=1,
+ mov_pattern=False):
+ """
+ Normal random walker with step size 1
+ :param diff_start: if True, walker starts walking
+ from a different position, else starts at (0,0)
+ :param total_steps: number of steps
+ :param step_size: defines the size of the steps
+ :param x_arr: empty numpy array consisting of n zeros
+ :param y_arr: empty numpy array consisting of n zeros
+ :param mov_pattern: boolean, if True, Moor'sche neighboorhood is used, else Neumann
+ :return: x, y numpy arrays
+ """
+
+ # checks which movement set the walker should get
+ # Neumann or Moor
+ if not mov_pattern:
+ # von Neumann neighborhood
+ direction_set = ("NORTH", "SOUTH", "EAST", "WEST")
+ else:
+ # Moor'sche neighborhood
+ direction_set = ("NORTH", "SOUTH", "EAST", "WEST",
+ "NORTHWEST", "NORTHEAST", "SOUTHWEST", "SOUTHEAST")
+
+ # get random start position if that is wanted
+ if diff_start is True:
+ start = (different_start_pos(total_steps))
+ else:
+ start = (0, 0)
+ # for the total number of steps, calculate walker movement randomly
+ for pos in range(1, total_steps):
+ direction = get_random_direction(direction_set)
+ x_arr, y_arr = next_step(x_arr, y_arr, pos, direction, step_size)
+ return x_arr + start[0], y_arr + start[1]
+
+
+def multiple_v_walkers(x_arr, y_arr, total_steps, total_walkers, step_size,
+ diff_start, mov_pattern):
+ """
+ Generates paths for multiple walkers
+ :param x_arr: np.array of x-coordinates (input: zeros)
+ :param y_arr: np.array of y-coordinates (input: zeros)
+ :param total_steps: number of steps of individual walker
+ :param total_walkers: number of walkers
+ :param step_size: defines the size of the steps
+ :param diff_start: bool value – if True, walkers start from different position
+ if False, walkers start from the same position
+ :param mov_pattern: boolean, if True, Moor'sche neighboorhood is used, else Neumann
+ :return: lists of x and y coordinates
+ """
+ # create empty lists
+ x_list = []
+ y_list = []
+ for _ in range(total_walkers):
+ # call walker function to generate array of x and y coordinates of one walker
+ x_axis, y_axis = v_walker(x_arr, y_arr, total_steps, diff_start, step_size, mov_pattern)
+ # append to list
+ x_list.append(x_axis)
+ y_list.append(y_axis)
+ # set input arrays back to zeros
+ x_arr, y_arr = create_walking_space(total_steps)
+ return x_list, y_list
+
+
+# plotting the walk
+def plot_v_walkers(total_steps, total_walkers, x_list, y_list):
+ """
+ Generates plot of walker(s) and saves figure as PNG file.
+ :param total_steps: Number of steps (needed for a figure title)
+ :param total_walkers: Number of walkers (needed for a figure title)
+ :param x_list: List of x-coordinates of walker(s) position
+ :param y_list: List of y-coordinates of walker(s) position
+ :return: none
+ """
+ # set figure and axis
+ fig = plt.figure(figsize=(5, 5), dpi=200)
+ axes = fig.add_subplot(111)
+ # create list of unique colors
+ color = iter(plt.cm.rainbow(np.linspace(0, 1, total_walkers)))
+ for wal in range(total_walkers):
+ col = next(color)
+ path_x = x_list[wal]
+ path_y = y_list[wal]
+ # plot vertices, path, start position and end position
+ axes.scatter(path_x, path_y, color=col, alpha=0.25, s=1)
+ axes.plot(path_x, path_y, color=col, alpha=0.25, lw=2, label='%s. walker' % (wal+1))
+ axes.plot(path_x[0], path_y[0], color=col, marker='o')
+ axes.plot(path_x[-1], path_y[-1], color=col, marker='+')
+ axes.axis('equal')
+ plt.xlabel('x')
+ plt.ylabel('x')
+ plt.legend()
+ plt.tight_layout()
+ plt.title("Random Walk (Number of walkers = " + str(total_walkers) +
+ ", $n = " + str(total_steps) + "$ steps)")
+ plt.savefig(".\\rand_walk_{}_{}.png".format(total_walkers, total_steps))
+ plt.show()