Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ ipython_config.py

# pyenv
.python-version

.idea
*.png
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
Expand Down
69 changes: 26 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,38 @@
# 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**.
## Implementation
The application is written in Python3 using Shapely to generate a Playground for multiple Random-Walkers.
Obstacles and borders are implemented as shapely polygons limiting the walking ranges of the walkers.

The Goal is to **practise writing readable, maintainable and reliable code collaboratively.**
Organized in 2 Modules:

## Group Exercise
- random_walker: Contains the default Random-Walker and different extensions, organized in subclasses. Each subclass follows specific rules when walking randomly over the Playground. Most subclasses are chess-inspired and follow the basic movement rules for five of the six stones on the board (sadly there is no Knight...).
- playground: Defining the area, in which the Walker is allowed to "play". A playground is seed-generated s.t. different obstacels can be implemented, while the default map simply spans an quadratic area that can be scaled via initial argument.

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)
## Application

2. This student invites the other student as a collaborator to the forked repository. Now you can both work on the code.
When running our Random-Walker-Application, a number of randomly chosen walkers will walk a set number of steps following their spefific rules. The resulting paths are drawn and returned as a plot.
For the execution of main.py you can set the following flags (if no flags are set during execution, default parameters are set for a number of 2 walkers on the default playground with scaling factor 4 -> playground size: 1000 x 1000):
- -h : show help message and how to use it
- -w : number of walkers {1...Inf}
- -n : space separated list of walker names to choose from {Rook,King,Bishop,Queen,Pawn}
- -ls: set playground scale factor {1...Inf}
- -ps : set playground/map generation seed (-> currently only one other playground available (seed=1) containing a hole, representing a lake) {0,1}
- -s : number of steps per walkers {1...Inf}
- --save : save the simulation to a file

3. Adapt the code to fulfil the requirements (see below).

4. Code review: Each group reviews the code of another group.
## Examples Configurations
![readmefigures/king.png](readmefigures/king.png)

5. Improve your code based on the review you got.
Simulate three Kings:
- python main.py -w 3 -n King

Simulate four walkers, choose from Queen and Pawn:
- python main.py -w 4 -n Queen Pawn

## 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.

![random_walk](rand_walk_100000.png)

The program works but it is not very readable. In addition, you should **extend the program based on the requirements listed below.

**Remember to apply the best practices in scientific computing** to make the code more readable, maintainable, reusable and efficient.

### Minimum requirements:

Extend the program so the following requirements are met:

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.

### Additional requirements:

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.

Be creative here! :)

## Code Review

Review the code of another group: (tuesday afternoon or wednesday morning)

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.
Simulate two random walkers for 10 steps:
- python main.py -w 2 -s 10

Simulate two random walkers on a map with obstacles:
- python main.py -w 2 -ps 1
83 changes: 52 additions & 31 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,61 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""A Random Walk Simulation """

# Python code for 2D random walk.
# Source: https://www.geeksforgeeks.org/random-walk-implementation-python/
import numpy
import argparse
from typing import List
import matplotlib.pyplot as plt
import random
import random_walker
from playground import Playground

# 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)
def main(steps: int, walkers: int, save: bool, map_seed: int, land_scale: int,
walker_types: List[str]):
"""
Execute the random walker simulation for a given set of parameters

# 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
else:
x[i] = x[i - 1]
y[i] = y[i - 1] - 1
:param steps: number of steps per walker
:param walkers: number of walkers
:param save: save the created figure
:param map_seed: map seed for the playground
:param land_scale: scale for the playground
:param walker_types: specified walker types
:return:
"""
# Create Playground and walkers
playground = Playground(seed=map_seed, scaling=land_scale)
walker_list = random_walker.create_different_walkers(walkers, steps, walker_types)
# Add Playground to plt
plt.title("Random Walk ($n = " + str(steps) + "$ steps)")
for x_positions, y_positions in playground.get_line_segments():
plt.plot(x_positions, y_positions, color='red')
# for each walker calculate the random walk and add to plt
for walker_index in range(walkers):
walker = walker_list[walker_index]
walker.execute_random_walk(playground)
# plotting the walk
plt.plot(walker.x_positions,
walker.y_positions,
label=str(type(walker).__name__) + ' index: ' + str(walker_index))
# show legend and plot
plt.legend()
# optional save the plot
if save:
Copy link
Copy Markdown
Collaborator

@BoSott BoSott Aug 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
python mian.py -w 3 -ls 200 -ps 2 -s 100
that is not how it should look like I guess? or is it?
the landscape scaling seems not to be taken into account when you plot the graph

plt.savefig("./rand_walk_{}.png".format(steps))
plt.show()


# plotting the walk
plt.title("Random Walk ($n = " + str(n) + "$ steps)")
plt.plot(x, y)
plt.savefig("./rand_walk_{}.png".format(n))
plt.show()
if __name__ == '__main__':
# Parse Arguments
parser = argparse.ArgumentParser(description='Executes and prints some random walkers')
parser.add_argument('-w', '--walkers', type=int, default=3, help='number of walkers')
parser.add_argument('-n', '--names', default=random_walker.get_walker_names(), nargs='+',
choices=random_walker.get_walker_names(),
help='space separated list of names of walker types to choose randomly')
parser.add_argument('-ls', '--landscale', type=int, default=4, help='playground scale')
parser.add_argument('-ps', '--playgroundseed', type=int, default=0,
choices=[0, 1], help='map generation seed')
parser.add_argument('-s', '--steps', type=int, default=100, help='number of steps per walker')
parser.add_argument('--save', action="store_true", help='save figure')
args = parser.parse_args()
# Execute Main
main(args.steps, args.walkers, args.save, args.playgroundseed, args.landscale, args.names)
94 changes: 94 additions & 0 deletions playground.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
""" Playground class for the random walk simulation """

from shapely.geometry import Polygon, Point


class Playground:
"""Defines a playground with obstacles for RandomWalkers"""
def __init__(self, scaling: int = 1, x_max: int = 250, y_max: int = 250, seed: int = 0):
"""
Create a new Playground

:param scaling: scale factor for x and y
:param x_max: x range
:param y_max: y range
:param seed: map seed
"""
self.x_max = scaling * x_max
self.y_max = scaling * y_max
x_max = self.x_max
y_max = self.y_max
border_polygon = [
(-x_max, -y_max),
(-x_max, y_max),
(x_max, y_max),
(x_max, -y_max),
]
holes = []
if seed == 1:
moon_lake = [
(x_max/2.5, y_max/2.5),
(x_max/2.5, -y_max/2.5),
(0, -y_max/2),
(0, -y_max/1.5),
(x_max*2/3, -y_max/1.5),
(x_max*3/4, 0),
(x_max * 1 / 2, y_max * 5 / 6),
(x_max/4, y_max*3/4),
(x_max / 2.5, y_max / 2.5)
]
holes.append(moon_lake)
''' In Development:
elif seed == 2:
outer_lake_border = [
(0, y_max/10),
(x_max/15, y_max/15),
(x_max / 10, 0),
(x_max / 15, -y_max / 15),
(0, -y_max / 10),
(-x_max / 15, -y_max / 15),
(-x_max / 10, 0),
(-x_max / 15, y_max / 15),
(0, y_max/10)
]
inner_lake_border = [
(0, y_max/30),
(x_max/25, y_max/25),
(x_max/30, 0),
(x_max/25, -y_max/25),
(0, -y_max/30),
(-x_max/25, -y_max/25),
(-x_max/30, 0),
(-x_max/25, y_max/25),
(0, y_max/30)
]
holes.append(outer_lake_border)
holes.append(inner_lake_border)
'''
self.holes = holes
self.shape = Polygon(border_polygon, holes)

def is_position_in_playground(self, x_position: float, y_position: float) -> bool:
"""
Check, whether the given walker position is valid on the playground

:param x_position: walker position
:param y_position: walker position
:return: True for a valid position, else False
"""
position = Point((x_position, y_position))
return self.shape.contains(position)

def get_line_segments(self) -> list:
"""
Get all parts from the playground in order to print them with py plot

:return: list of polygon border
"""
result = []
for hole in self.holes:
poly = Polygon(hole)
result.append(poly.exterior.xy)
result.append(self.shape.exterior.xy)
return result
Binary file modified rand_walk_100000.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading