Merge branch 'master' of ssh://daknuett.eu:10022/daknuett/scientific-programming-exercises
This commit is contained in:
commit
8679f25e82
36
exam/ex06/finite_state_machine.py
Normal file
36
exam/ex06/finite_state_machine.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
|
||||||
|
class FiniteStateMachine(object):
|
||||||
|
def __init__(self, start: int, valid: list, default: int, transitions: dict):
|
||||||
|
self._start = start
|
||||||
|
self._valid = valid
|
||||||
|
|
||||||
|
self._transitions = dict()
|
||||||
|
for state, trans in transitions.items():
|
||||||
|
self._transitions[state] = dict()
|
||||||
|
for words, target in trans.items():
|
||||||
|
for word in words:
|
||||||
|
self._transitions[state][word] = target
|
||||||
|
|
||||||
|
self._default = default
|
||||||
|
self._state = start
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self._state = self._start
|
||||||
|
|
||||||
|
def make_transition(self, word):
|
||||||
|
if(not self._state in self._transitions):
|
||||||
|
self._state = self._default
|
||||||
|
if(not word in self._transitions[self._state]):
|
||||||
|
self._state = self._default
|
||||||
|
return
|
||||||
|
|
||||||
|
self._state = self._transitions[self._state][word]
|
||||||
|
|
||||||
|
def check(self, sequence):
|
||||||
|
for word in sequence:
|
||||||
|
self.make_transition(word)
|
||||||
|
is_valid = self._state in self._valid
|
||||||
|
self.reset()
|
||||||
|
return is_valid
|
||||||
|
|
16
exam/ex06/main.py
Normal file
16
exam/ex06/main.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
from parser import Parser
|
||||||
|
from tokenio import TokenStream
|
||||||
|
from tokens import NumberTokenParser
|
||||||
|
|
||||||
|
texts = ["one plus one"
|
||||||
|
, "one plus two"
|
||||||
|
, "thirtytwo plus eleven"
|
||||||
|
, "four times four"
|
||||||
|
, "(eight plus eleven) times two"
|
||||||
|
, "twohundred through eleven"]
|
||||||
|
|
||||||
|
for text in texts:
|
||||||
|
print(text, "=", Parser(TokenStream(StringIO(text))).parse())
|
||||||
|
|
91
exam/ex06/parser.py
Normal file
91
exam/ex06/parser.py
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
from tokens import NumberTokenParser, OperatorTokenParser
|
||||||
|
|
||||||
|
class ParsingException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Parser(object):
|
||||||
|
def __init__(self, token_stream):
|
||||||
|
self._token_stream = token_stream
|
||||||
|
self._stack = deque()
|
||||||
|
self._current_list = deque()
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
state = 0
|
||||||
|
while True:
|
||||||
|
token = self._token_stream.get_token()
|
||||||
|
if(token == "("):
|
||||||
|
if(state == 1):
|
||||||
|
raise ParsingException(
|
||||||
|
"expected operator, not parenthesis: {} (near '{}')".format(
|
||||||
|
self._token_stream._offset
|
||||||
|
, token))
|
||||||
|
|
||||||
|
self._stack.append(self._current_list)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if(NumberTokenParser.can_parse(token)):
|
||||||
|
if(state == 1):
|
||||||
|
raise ParsingException(
|
||||||
|
"expected operator, not number: {} (near '{}')".format(
|
||||||
|
self._token_stream._offset
|
||||||
|
, token))
|
||||||
|
self._current_list.append(NumberTokenParser(token).parse())
|
||||||
|
state = 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if(OperatorTokenParser.can_parse(token)):
|
||||||
|
if(state != 1):
|
||||||
|
raise ParsingException(
|
||||||
|
"expected number or parenthesis, not operator: {} (near '{}')".format(
|
||||||
|
self._token_stream._offset
|
||||||
|
, token))
|
||||||
|
|
||||||
|
self._current_list.append(OperatorTokenParser(token).parse())
|
||||||
|
state = 0
|
||||||
|
continue
|
||||||
|
|
||||||
|
if(token == ")"):
|
||||||
|
#if(state == 1):
|
||||||
|
# raise ParsingException(
|
||||||
|
# "expected operator, not parenthesis: {} (near '{}')".format(
|
||||||
|
# self._token_stream._offset
|
||||||
|
# , token))
|
||||||
|
state = 1
|
||||||
|
|
||||||
|
result = self.execute_branch(self._current_list)
|
||||||
|
self._current_list = self._stack.pop()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if(not token):
|
||||||
|
if(self._stack):
|
||||||
|
raise ParsingException("unexpected EOF while parsing")
|
||||||
|
return self.execute_branch(self._current_list)
|
||||||
|
|
||||||
|
raise ParsingException("unknown token: {} (near '{}')".format(self._token_stream._offset, token))
|
||||||
|
|
||||||
|
return self.execute_branch(self._current_list)
|
||||||
|
|
||||||
|
def execute_branch(self, branch):
|
||||||
|
result = None
|
||||||
|
current_operator = None
|
||||||
|
|
||||||
|
for element in branch:
|
||||||
|
if(result is None):
|
||||||
|
result = element
|
||||||
|
continue
|
||||||
|
|
||||||
|
if(not isinstance(element, (float, int, complex))):
|
||||||
|
# Operator
|
||||||
|
current_operator = element
|
||||||
|
continue
|
||||||
|
|
||||||
|
if(current_operator):
|
||||||
|
result = current_operator(result, element)
|
||||||
|
current_operator = None
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
56
exam/ex06/tokenio.py
Normal file
56
exam/ex06/tokenio.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
base_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
end_of_token_chars = "() \t"
|
||||||
|
whitespace_chars = " \t"
|
||||||
|
|
||||||
|
class UnexpectedCharacterException(Exception):
|
||||||
|
def __init__(self, msg, offset, char, *args):
|
||||||
|
Exception.__init__(self, *args)
|
||||||
|
self._msg = msg
|
||||||
|
self._offset = offset
|
||||||
|
self._char = char
|
||||||
|
|
||||||
|
|
||||||
|
class TokenStream(object):
|
||||||
|
def __init__(self, file_):
|
||||||
|
self._file = file_
|
||||||
|
self._file.seek(0, 0)
|
||||||
|
self._offset = 0
|
||||||
|
|
||||||
|
def _getc(self):
|
||||||
|
c = self._file.read(1)
|
||||||
|
if(c):
|
||||||
|
self._offset += 1
|
||||||
|
return c
|
||||||
|
def _ungetc(self):
|
||||||
|
self._file.seek(self._offset - 1, 0)
|
||||||
|
self._offset -= 1
|
||||||
|
|
||||||
|
def get_token(self):
|
||||||
|
result = deque()
|
||||||
|
while True:
|
||||||
|
c = self._getc()
|
||||||
|
if(not c):
|
||||||
|
# EOF.
|
||||||
|
break
|
||||||
|
if(c in base_chars):
|
||||||
|
result.append(c)
|
||||||
|
continue
|
||||||
|
if(c in end_of_token_chars):
|
||||||
|
if(not result):
|
||||||
|
# We are not inside a token.
|
||||||
|
if(c in whitespace_chars):
|
||||||
|
# Some whitespace. Ignore it.
|
||||||
|
continue
|
||||||
|
# A parenthesis.
|
||||||
|
return c
|
||||||
|
|
||||||
|
# End of token.
|
||||||
|
self._ungetc()
|
||||||
|
break
|
||||||
|
raise UnexpectedCharacterException("Unexpected character while tokenizing", self._offset, c)
|
||||||
|
return "".join(result)
|
||||||
|
|
||||||
|
|
||||||
|
|
155
exam/ex06/tokens.py
Normal file
155
exam/ex06/tokens.py
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
from collections import deque
|
||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
from finite_state_machine import FiniteStateMachine
|
||||||
|
|
||||||
|
BASE_NUMBER_TOKENS = {"one": 1
|
||||||
|
, "two": 2
|
||||||
|
, "three": 3
|
||||||
|
, "four": 4
|
||||||
|
, "five": 5
|
||||||
|
, "six": 6
|
||||||
|
, "seven": 7
|
||||||
|
, "eight": 8
|
||||||
|
, "nine": 9}
|
||||||
|
DECI_NUMBER_TOKENS = {"twenty": 20
|
||||||
|
, "thirty": 30
|
||||||
|
, "fourty": 40
|
||||||
|
, "fifty": 50
|
||||||
|
, "sixty": 60
|
||||||
|
, "secenty": 70
|
||||||
|
, "eigthy": 80
|
||||||
|
, "ninety": 90}
|
||||||
|
TEEN_NUMBER_TOKENS = {"ten": 10
|
||||||
|
, "eleven": 11
|
||||||
|
, "twelve": 12
|
||||||
|
, "thirteen": 13
|
||||||
|
, "fourteen": 14
|
||||||
|
, "fifteen": 15
|
||||||
|
, "sixteen": 16
|
||||||
|
, "seventeen": 17
|
||||||
|
, "eighteen": 18
|
||||||
|
, "nineteen": 19}
|
||||||
|
HUNDRED_NUMBER_TOKENS = {"hundred": 100}
|
||||||
|
ZERO_NUMBER_TOKENS = {"zero": 0
|
||||||
|
, "null": 0}
|
||||||
|
|
||||||
|
OPERATOR_TOKENS = { "plus": lambda x,y: x + y
|
||||||
|
, "minus": lambda x,y: x - y
|
||||||
|
, "times": lambda x,y: x * y
|
||||||
|
, "through": lambda x,y: x / y}
|
||||||
|
|
||||||
|
transitions = {
|
||||||
|
0: { tuple(ZERO_NUMBER_TOKENS) + tuple(TEEN_NUMBER_TOKENS): 1
|
||||||
|
, tuple(DECI_NUMBER_TOKENS): 2
|
||||||
|
, tuple(BASE_NUMBER_TOKENS): 3}
|
||||||
|
, 2: {tuple(BASE_NUMBER_TOKENS): 1}
|
||||||
|
, 3: {tuple(HUNDRED_NUMBER_TOKENS): 4}
|
||||||
|
, 4: {tuple(DECI_NUMBER_TOKENS): 2}
|
||||||
|
}
|
||||||
|
valid_states = [1, 2, 3, 4]
|
||||||
|
default_transition = -1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TokenParsingException(Exception):
|
||||||
|
pass
|
||||||
|
class SubtokenizingException(TokenParsingException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TokenParser(metaclass = ABCMeta):
|
||||||
|
@classmethod
|
||||||
|
def can_parse(cls, token):
|
||||||
|
try:
|
||||||
|
cls(token).parse()
|
||||||
|
return True
|
||||||
|
except TokenParsingException:
|
||||||
|
return False
|
||||||
|
@abstractmethod
|
||||||
|
def parse(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NumberTokenParser(TokenParser):
|
||||||
|
def __init__(self, token):
|
||||||
|
self._token = token.lower()
|
||||||
|
self._token_length = len(token)
|
||||||
|
|
||||||
|
self._finite_state_machine = FiniteStateMachine(0, valid_states, default_transition, transitions)
|
||||||
|
|
||||||
|
def get_token_of_class_or_none(self, offset, token_class):
|
||||||
|
for token in token_class:
|
||||||
|
if(len(token) + offset > self._token_length):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if(self._token[offset: offset + len(token)] == token):
|
||||||
|
return token
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_next_token_part(self, offset):
|
||||||
|
token_classes = [ZERO_NUMBER_TOKENS
|
||||||
|
, HUNDRED_NUMBER_TOKENS
|
||||||
|
, TEEN_NUMBER_TOKENS
|
||||||
|
, DECI_NUMBER_TOKENS
|
||||||
|
, BASE_NUMBER_TOKENS]
|
||||||
|
|
||||||
|
result = None
|
||||||
|
for token_class in token_classes:
|
||||||
|
result = self.get_token_of_class_or_none(offset, token_class)
|
||||||
|
if(result):
|
||||||
|
break
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def subtokenize(self):
|
||||||
|
token_parts = deque()
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
while(True):
|
||||||
|
subtoken = self.get_next_token_part(offset)
|
||||||
|
if(subtoken is None):
|
||||||
|
if(offset != self._token_length):
|
||||||
|
raise SubtokenizingException("part of the token is dangling: '{}'".format(self._token[offset:]))
|
||||||
|
return list(token_parts)
|
||||||
|
|
||||||
|
offset += len(subtoken)
|
||||||
|
token_parts.append(subtoken)
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
token_parts = self.subtokenize()
|
||||||
|
|
||||||
|
if(not self._finite_state_machine.check(token_parts)):
|
||||||
|
raise TokenParsingException("token '{}' is invalid".format(self._token))
|
||||||
|
|
||||||
|
|
||||||
|
# This is ugly but it works.
|
||||||
|
result = 0
|
||||||
|
for subtoken in token_parts:
|
||||||
|
if(subtoken in BASE_NUMBER_TOKENS):
|
||||||
|
result += BASE_NUMBER_TOKENS[subtoken]
|
||||||
|
if(subtoken in TEEN_NUMBER_TOKENS):
|
||||||
|
result += TEEN_NUMBER_TOKENS[subtoken]
|
||||||
|
if(subtoken in DECI_NUMBER_TOKENS):
|
||||||
|
result += DECI_NUMBER_TOKENS[subtoken]
|
||||||
|
|
||||||
|
if(subtoken in HUNDRED_NUMBER_TOKENS):
|
||||||
|
result *= HUNDRED_NUMBER_TOKENS[subtoken]
|
||||||
|
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class OperatorTokenParser(TokenParser):
|
||||||
|
def __init__(self, token):
|
||||||
|
self._token = token.lower()
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
if(not self._token in OPERATOR_TOKENS):
|
||||||
|
raise TokenParsingException("token '{}' is not an operator".format(self._token))
|
||||||
|
return OPERATOR_TOKENS[self._token]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
49
exam/ex08/main.py
Normal file
49
exam/ex08/main.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
class BrownIterator(object):
|
||||||
|
def __init__(self, N, m):
|
||||||
|
self._N = N
|
||||||
|
self._max_m = m
|
||||||
|
self._i = 0
|
||||||
|
self._xs = None
|
||||||
|
self._ys = None
|
||||||
|
def __iter__(self):
|
||||||
|
self._xs = np.zeros(self._N) + 1.5
|
||||||
|
self._ys = np.zeros(self._N)
|
||||||
|
self._i = 0
|
||||||
|
return self
|
||||||
|
def __next__(self):
|
||||||
|
self._i += 1
|
||||||
|
if(self._i > self._max_m):
|
||||||
|
raise StopIteration()
|
||||||
|
if(self._i == 1):
|
||||||
|
return self._xs, self._ys
|
||||||
|
|
||||||
|
theta = np.random.uniform(0, np.pi * 2, self._N)
|
||||||
|
|
||||||
|
self._xs = self._xs + np.cos(theta)
|
||||||
|
self._ys = self._ys + np.sin(theta)
|
||||||
|
|
||||||
|
# Reflect the particles
|
||||||
|
self._xs[self._xs > 3] += 1.5 * (3 - self._xs[self._xs > 3])
|
||||||
|
self._ys[self._ys > 3] += 1.5 * (3 - self._ys[self._ys > 3])
|
||||||
|
self._xs[self._xs < -3] += 1.5 * (-3 - self._xs[self._xs < -3])
|
||||||
|
self._ys[self._ys < -3] += 1.5 * (-3 - self._ys[self._ys < -3])
|
||||||
|
|
||||||
|
return self._xs, self._ys
|
||||||
|
|
||||||
|
if( __name__ == "__main__"):
|
||||||
|
data = np.array([i for i in BrownIterator(1000, 251)])
|
||||||
|
print(data)
|
||||||
|
|
||||||
|
p1, = plt.plot(data[5,0], data[5,1], "r.", label="t = 5")
|
||||||
|
p2, = plt.plot(data[25,0], data[25,1], "y.", label="t = 25")
|
||||||
|
p3, = plt.plot(data[50,0], data[50,1], "b.", label="t = 50")
|
||||||
|
p4, = plt.plot(data[250,0], data[250,1], "g.", label="t = 250")
|
||||||
|
|
||||||
|
plt.legend(handles=[p1, p2, p3, p4])
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
64
exam/ex10/main.py
Normal file
64
exam/ex10/main.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
class StochastikIterator(object):
|
||||||
|
def __init__(self, r, num_simulations, chi0, std_deviation, maxiter=None):
|
||||||
|
self._r = r
|
||||||
|
self._num_simulations = num_simulations
|
||||||
|
self._chi0 = chi0
|
||||||
|
self._std_deviation = std_deviation
|
||||||
|
self._maxiter=maxiter
|
||||||
|
self._chi = None
|
||||||
|
self._currentiter = None
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
self._currentiter = 0
|
||||||
|
self._chi = np.array([self._chi0] * self._num_simulations, dtype=np.float64)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
if(self._maxiter and self._currentiter == self._maxiter):
|
||||||
|
raise StopIteration()
|
||||||
|
self._currentiter += 1
|
||||||
|
|
||||||
|
|
||||||
|
result = self._chi.copy()
|
||||||
|
self._chi *= np.exp(r - np.random.normal(scale=self._std_deviation, size=self._num_simulations))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def do(self, iters=None):
|
||||||
|
if(not (iters or self._maxiter)):
|
||||||
|
raise ValueError("iters or maxiter must be specified")
|
||||||
|
|
||||||
|
if(iters):
|
||||||
|
until = iters
|
||||||
|
if(self._maxiter and (not iters or (iters and self._maxiter < iters))):
|
||||||
|
until = self._maxiter
|
||||||
|
|
||||||
|
|
||||||
|
self._currentiter = 0
|
||||||
|
self._chi = np.array([self._chi0] * self._num_simulations, dtype=np.float64)
|
||||||
|
for i in range(until):
|
||||||
|
self._chi *= np.exp(r - np.random.normal(scale=self._std_deviation, size=self._num_simulations))
|
||||||
|
|
||||||
|
return self._chi.copy()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mean_chi = lambda r: np.mean(StochastikIterator(r, 500, 100, 0.25).do(70))
|
||||||
|
|
||||||
|
for r in [0.05, 0, -0.05]:
|
||||||
|
print("r =", r, "\t<chi> = ", mean_chi(r))
|
||||||
|
|
||||||
|
|
||||||
|
R = np.arange(-4, 4, 0.01)
|
||||||
|
|
||||||
|
M = [mean_chi(r) for r in R]
|
||||||
|
|
||||||
|
plt.plot(R, M, label="$<\\chi>$")
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
67
exam/ex13/backend.py
Normal file
67
exam/ex13/backend.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
CELL_IS_ALIVE = 0b1
|
||||||
|
CELL_WILL_BE_ALIVE = 0b10
|
||||||
|
CELL_WILL_BE_DEAD = 0b100
|
||||||
|
|
||||||
|
def is_alive(model, i, j):
|
||||||
|
return model[i][j] & CELL_IS_ALIVE
|
||||||
|
|
||||||
|
def get_cells_around(model, i, j):
|
||||||
|
# Most common case. Handle this first.
|
||||||
|
# Fortunately the most common case is also the fastets to handle.
|
||||||
|
if(i > 2 and j > 2 and i < model.shape[0] and j < model.shape[1]):
|
||||||
|
return model[i - 2: i + 2, j - 2: j + 2]
|
||||||
|
|
||||||
|
# Neither negative indices nor indices that overflow
|
||||||
|
# are handled properly by numpy. So basically I have to do that manually:
|
||||||
|
cells_around = np.zeros([3, 3], np.int8)
|
||||||
|
for k, m in enumerate(range(i - 1, i + 2)):
|
||||||
|
for l, n in enumerate(range(j - 1, j + 2)):
|
||||||
|
real_m = m
|
||||||
|
if(m >= model.shape[0]):
|
||||||
|
real_m = m - model.shape[0]
|
||||||
|
if(m < 0):
|
||||||
|
real_m = m + model.shape[0]
|
||||||
|
real_n = n
|
||||||
|
if(n >= model.shape[1]):
|
||||||
|
real_n = n - model.shape[1]
|
||||||
|
if(n < 0):
|
||||||
|
real_n = n + model.shape[1]
|
||||||
|
cells_around[k][l] = model[real_m][real_n]
|
||||||
|
return cells_around
|
||||||
|
|
||||||
|
def count_living_cells_around(model, i, j):
|
||||||
|
cells_around = get_cells_around(model, i, j)
|
||||||
|
living_cells_around = (cells_around & CELL_IS_ALIVE) == 1
|
||||||
|
living_cells_around[1][1] = False
|
||||||
|
unique, counts = np.unique(living_cells_around, return_counts=True)
|
||||||
|
if(True not in unique):
|
||||||
|
return 0
|
||||||
|
return counts[np.where(unique == True)[0]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def handle_cell(model, i, j):
|
||||||
|
living_cells_around = count_living_cells_around(model, i, j)
|
||||||
|
if(not is_alive(model, i, j)):
|
||||||
|
if(living_cells_around == 3):
|
||||||
|
model[i][j] = CELL_WILL_BE_ALIVE
|
||||||
|
return
|
||||||
|
|
||||||
|
if(living_cells_around > 3 or living_cells_around < 2):
|
||||||
|
model[i][j] = CELL_WILL_BE_DEAD
|
||||||
|
|
||||||
|
def after_tick_finished(model):
|
||||||
|
# kill the cells that have died
|
||||||
|
model[(model & CELL_WILL_BE_DEAD) != 0] = 0
|
||||||
|
|
||||||
|
# create the new cells
|
||||||
|
model[(model & CELL_WILL_BE_ALIVE) != 0] = 1
|
||||||
|
|
||||||
|
def total_living_cells(model):
|
||||||
|
living_cells = (model & CELL_IS_ALIVE) == 1
|
||||||
|
unique, counts = np.unique(living_cells, return_counts=True)
|
||||||
|
if(True not in unique):
|
||||||
|
return 0
|
||||||
|
return counts[np.where(unique == True)[0]]
|
11
exam/ex13/executor.py
Normal file
11
exam/ex13/executor.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from backend import handle_cell, after_tick_finished
|
||||||
|
|
||||||
|
def execute_tick(model):
|
||||||
|
n, m = model.shape
|
||||||
|
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(m):
|
||||||
|
handle_cell(model, i, j)
|
||||||
|
after_tick_finished(model)
|
||||||
|
|
||||||
|
|
28
exam/ex13/main.py
Normal file
28
exam/ex13/main.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from model import prepare_model
|
||||||
|
from backend import total_living_cells
|
||||||
|
from executor import execute_tick
|
||||||
|
|
||||||
|
def cells_alive_at_ticks(model, ticks):
|
||||||
|
for i in range(ticks):
|
||||||
|
cell_count = total_living_cells(model)
|
||||||
|
yield [i, cell_count]
|
||||||
|
execute_tick(model)
|
||||||
|
|
||||||
|
plot_data = {
|
||||||
|
0.3 : ("r", "p=0.3")
|
||||||
|
, 0.5: ("g", "p=0.5")
|
||||||
|
, 0.7: ("b", "p=0.7")
|
||||||
|
}
|
||||||
|
|
||||||
|
handles = []
|
||||||
|
for p, (color, label) in plot_data.items():
|
||||||
|
model = prepare_model(100, p)
|
||||||
|
living_cells = np.array(list(cells_alive_at_ticks(model, 10)))
|
||||||
|
plot, = plt.plot(living_cells[:, 0], living_cells[:, 1], color, label=label)
|
||||||
|
handles.append(plot)
|
||||||
|
|
||||||
|
plt.legend(handles=handles)
|
||||||
|
plt.show()
|
8
exam/ex13/model.py
Normal file
8
exam/ex13/model.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def prepare_model(n, p):
|
||||||
|
"""
|
||||||
|
p must be between 0 and 1.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (np.random.rand(n, n) < p).astype(np.int8)
|
34
exam/ex14/main.py
Normal file
34
exam/ex14/main.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import matplotlib.animation as ani
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
sys.path.append("../ex13/")
|
||||||
|
|
||||||
|
|
||||||
|
from model import prepare_model
|
||||||
|
from backend import CELL_IS_ALIVE
|
||||||
|
from executor import execute_tick
|
||||||
|
|
||||||
|
fig = plt.figure(figsize=(7, 7))
|
||||||
|
ax = fig.add_axes([0, 0, 1, 1], frameon=False)
|
||||||
|
ax.set_xlim(0, 100)
|
||||||
|
ax.set_xticks([])
|
||||||
|
ax.set_ylim(0, 100)
|
||||||
|
ax.set_yticks([])
|
||||||
|
|
||||||
|
|
||||||
|
model = prepare_model(100, 0.2)
|
||||||
|
scat, = ax.plot(*np.where((model & CELL_IS_ALIVE) == 1), "s", color="black")
|
||||||
|
|
||||||
|
frames = 100
|
||||||
|
|
||||||
|
def update(i):
|
||||||
|
execute_tick(model)
|
||||||
|
scat.set_data(*np.where((model & CELL_IS_ALIVE) == 1))
|
||||||
|
print("%.2f" % ((i / frames) * 100), "%", end="\r")
|
||||||
|
|
||||||
|
animation = ani.FuncAnimation(fig, update, range(frames), interval=1)
|
||||||
|
animation.save("output/animation.gif", dpi=80, writer='imagemagick')
|
||||||
|
print("\noutput/animation.gif")
|
122
exam/ex16/geometry.py
Normal file
122
exam/ex16/geometry.py
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
from abc import abstractmethod, ABCMeta
|
||||||
|
from itertools import permutations
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class Point(object):
|
||||||
|
__slots__ = ["x", "y"]
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
def __abs__(self):
|
||||||
|
return (self.x**2 + self.y**2)**0.5
|
||||||
|
def __add__(self, other):
|
||||||
|
if(isinstance(self, Point)):
|
||||||
|
return Point(self.x + other.x, self.y + other.y)
|
||||||
|
raise TypeError("cannot add {} and {}".format(type(other), type(self)))
|
||||||
|
def __sub__(self, other):
|
||||||
|
if(isinstance(self, Point)):
|
||||||
|
return Point(self.x - other.x, self.y - other.y)
|
||||||
|
raise TypeError("cannot subtract {} and {}".format(type(other), type(self)))
|
||||||
|
|
||||||
|
def rotate(self, angle):
|
||||||
|
return Point(self.x * np.cos(angle) - self.y * np.sin(angle)
|
||||||
|
, self.x * np.sin(angle) + self.y * np.cos(angle))
|
||||||
|
def __repr__(self):
|
||||||
|
return "{}({}, {})".format(type(self).__name__, self.x, self.y)
|
||||||
|
|
||||||
|
|
||||||
|
class TwoDimensionalObject(metaclass=ABCMeta):
|
||||||
|
@abstractmethod
|
||||||
|
def contains_point(self, point: Point):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __contains__(self, other):
|
||||||
|
if(isinstance(other, Point)):
|
||||||
|
return self.contains_point(other)
|
||||||
|
raise TypeError("unable to check if {} is in {}".format(type(other), type(self)))
|
||||||
|
|
||||||
|
|
||||||
|
class Circle(TwoDimensionalObject):
|
||||||
|
def __init__(self, origin: Point, radius):
|
||||||
|
self.origin = origin
|
||||||
|
self.radius = radius
|
||||||
|
|
||||||
|
def contains_point(self, point: Point):
|
||||||
|
return abs(self.origin - point) < self.radius
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "{}({}, {})".format(type(self).__name__, self.origin, self.radius)
|
||||||
|
|
||||||
|
class Rectangle(TwoDimensionalObject):
|
||||||
|
"""
|
||||||
|
A Rectangle is constructed as follows:
|
||||||
|
The Points p1, p2 are connected using orthogonal lines::
|
||||||
|
|
||||||
|
p1 +-------+
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
+-------+ p2
|
||||||
|
|
||||||
|
and then the Rectangle is rotated ``angle`` degrees around ``p1``.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, p1: Point, p2: Point, angle=0):
|
||||||
|
self.p1 = p1
|
||||||
|
self.p2 = p2
|
||||||
|
|
||||||
|
self.local_p1 = Point(0, 0)
|
||||||
|
self.local_p2 = (p2 - p1).rotate(angle)
|
||||||
|
|
||||||
|
self.angle = angle
|
||||||
|
|
||||||
|
def contains_point(self, point: Point):
|
||||||
|
point_in_eigen_frame = (point - self.p1).rotate(self.angle)
|
||||||
|
|
||||||
|
if(self.local_p1.x < self.local_p2.x):
|
||||||
|
if(point_in_eigen_frame.x < self.local_p1.x or point_in_eigen_frame.x > self.local_p1.x):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if(point_in_eigen_frame.x > self.local_p1.x or point_in_eigen_frame.x < self.local_p1.x):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if(self.local_p1.y < self.local_p2.y):
|
||||||
|
if(point_in_eigen_frame.y < self.local_p1.y or point_in_eigen_frame.y > self.local_p1.y):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if(point_in_eigen_frame.y > self.local_p1.y or point_in_eigen_frame.y < self.local_p1.y):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
def __repr__(self):
|
||||||
|
return "{}({}, {}, angle={})".format(type(self).__name__, self.p1, self.p2, self.angle)
|
||||||
|
|
||||||
|
|
||||||
|
class Triangle(TwoDimensionalObject):
|
||||||
|
def __init__(self, p1: Point, p2: Point, p3: Point):
|
||||||
|
self.p1 = p1
|
||||||
|
self.p2 = p2
|
||||||
|
self.p3 = p3
|
||||||
|
def contains_point(self, point: Point):
|
||||||
|
points = [self.p1, self.p2, self.p3]
|
||||||
|
triangle_surface = abs(sum([points[i].x * (points[(i + 1) % 3].y - points[(i + 2) % 3].y) for i in range(3)]))
|
||||||
|
|
||||||
|
points = [point, self.p1, self.p2, self.p3]
|
||||||
|
surfaces = [abs(sum([p[i].x * (p[(i + 1) % 4].y - p[(i + 2) % 4].y) for i in range(4)])) for p in permutations(points)]
|
||||||
|
|
||||||
|
return max(surfaces) < triangle_surface
|
||||||
|
def __repr__(self):
|
||||||
|
return "{}({}, {}, {})".format(type(self).__name__, self.p1, self.p2, self.p3)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionOfFigures(object):
|
||||||
|
def __init__(self, figures):
|
||||||
|
self.figures = figures
|
||||||
|
|
||||||
|
def containing(self, point: Point):
|
||||||
|
return [f for f in self.figures if point in f]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "{}({})".format(type(self).__name__, repr(self.figures))
|
21
exam/ex16/main.py
Normal file
21
exam/ex16/main.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
from geometry import Point, Rectangle, Triangle, Circle, CollectionOfFigures
|
||||||
|
|
||||||
|
|
||||||
|
c1 = Circle(Point(0, 0), 1)
|
||||||
|
c2 = Circle(Point(4, 4), 2)
|
||||||
|
r1 = Rectangle(Point(0, 0), Point(4, 4))
|
||||||
|
r2 = Rectangle(Point(-3, -3), Point(-1, -1))
|
||||||
|
t1 = Triangle(Point(-1, -1), Point(-1, 1), Point(0, 1))
|
||||||
|
t2 = Triangle(Point(0, 0), Point(6, 0), Point(0, 6))
|
||||||
|
t3 = Triangle(Point(-5, -5), Point(0, 6), Point(5, -5))
|
||||||
|
|
||||||
|
collection = CollectionOfFigures([c1, c2, r1, r2, t1, t2, t3])
|
||||||
|
|
||||||
|
p1 = Point(4, 4)
|
||||||
|
p2 = Point(-1, 0)
|
||||||
|
p3 = Point(0, 0)
|
||||||
|
|
||||||
|
print(p1, "is in", collection.containing(p1))
|
||||||
|
print(p2, "is in", collection.containing(p2))
|
||||||
|
print(p3, "is in", collection.containing(p3))
|
32
exam/ex19/caesar.py
Normal file
32
exam/ex19/caesar.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
|
||||||
|
def internal_ord(c):
|
||||||
|
c = c.lower()
|
||||||
|
internal_ord = ord(c) - ord("a")
|
||||||
|
if(internal_ord < 0 or internal_ord > 25):
|
||||||
|
raise ValueError("'{}' is an unsupported character".format(c))
|
||||||
|
return internal_ord
|
||||||
|
|
||||||
|
def internal_chr(i):
|
||||||
|
return chr(ord("a") + i)
|
||||||
|
|
||||||
|
|
||||||
|
def encode_or_keeps_space(c):
|
||||||
|
if(c == " "):
|
||||||
|
return (False, c)
|
||||||
|
return (True, internal_ord(c))
|
||||||
|
|
||||||
|
def prepare_string(s):
|
||||||
|
return (encode_or_keeps_space(c) for c in s)
|
||||||
|
|
||||||
|
|
||||||
|
def _caesar(s, K):
|
||||||
|
for encode, i in prepare_string(s):
|
||||||
|
if(encode):
|
||||||
|
yield internal_chr((i + K) % 26)
|
||||||
|
else:
|
||||||
|
yield i
|
||||||
|
|
||||||
|
def caesar(s, K):
|
||||||
|
return "".join(_caesar(s, K))
|
||||||
|
|
21
exam/ex19/main.py
Normal file
21
exam/ex19/main.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from caesar import caesar
|
||||||
|
from statistical_attack import get_statistical_key
|
||||||
|
|
||||||
|
text1 = "hello world this is a test"
|
||||||
|
text2 = "this is a message that is obfuscated"
|
||||||
|
|
||||||
|
K2 = 4
|
||||||
|
K1 = 13
|
||||||
|
|
||||||
|
print(caesar(text1, K1))
|
||||||
|
print(caesar(caesar(text1, K1), K1))
|
||||||
|
|
||||||
|
print(caesar(text2, K2))
|
||||||
|
print(caesar(caesar(text2, K2), abs(K2 - 26)))
|
||||||
|
|
||||||
|
text4 = "In cryptography, a Caesar cipher, also known as Caesar's cipher, the shift cipher, Caesar's code or Caesar shift, is one of the simplest and most widely known encryption techniques. It is a type of substitution cipher in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, with a left shift of 3, D would be replaced by A, E would become B, and so on. The method is named after Julius Caesar, who used it in his private correspondence"
|
||||||
|
|
||||||
|
text4 = "".join((s for s in text4 if s in " abcdefghijklmnopqrstuvwxyz"))
|
||||||
|
|
||||||
|
print(get_statistical_key(caesar(text4, K2)))
|
||||||
|
print(get_statistical_key(caesar(text2, K2)))
|
12326
exam/ex19/sample.tx
Normal file
12326
exam/ex19/sample.tx
Normal file
File diff suppressed because it is too large
Load Diff
29
exam/ex19/statistical_attack.py
Normal file
29
exam/ex19/statistical_attack.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
reference = Counter((c for c in open("sample.tx").read().lower() if c in alphabet))
|
||||||
|
|
||||||
|
def get_statistics(text):
|
||||||
|
return Counter([c for c in text.lower() if c in alphabet])
|
||||||
|
|
||||||
|
def relative_most_common(statistics):
|
||||||
|
c, abs_probability = statistics.most_common(1)[0]
|
||||||
|
|
||||||
|
total_chrs = sum([v for k,v in statistics.most_common()])
|
||||||
|
return abs_probability / total_chrs
|
||||||
|
|
||||||
|
|
||||||
|
def get_statistical_key(text):
|
||||||
|
statistics = get_statistics(text)
|
||||||
|
|
||||||
|
quality = relative_most_common(statistics) / relative_most_common(reference)
|
||||||
|
|
||||||
|
c, abs_probability = statistics.most_common(1)[0]
|
||||||
|
|
||||||
|
K = abs(ord("e") - ord(c))
|
||||||
|
|
||||||
|
if(quality > 1):
|
||||||
|
quality = relative_most_common(reference) / relative_most_common(statistics)
|
||||||
|
|
||||||
|
return K, quality
|
13
exam/ex26/main.py
Normal file
13
exam/ex26/main.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
from polynomial import Polynomial
|
||||||
|
|
||||||
|
p1 = Polynomial([0, 2, 3, 4, 0, 0, 9])
|
||||||
|
p2 = Polynomial([0, 0, 1])
|
||||||
|
p3 = Polynomial([1, 0, 0, 1])
|
||||||
|
|
||||||
|
print("p1 = ", p1)
|
||||||
|
print("p2 = ", p2)
|
||||||
|
print("p3 = ", p3)
|
||||||
|
|
||||||
|
print("p1 + p3 = ", p1 + p3)
|
||||||
|
print("p2 * p3 = ", p2 * p3)
|
||||||
|
print("p1 * p3 = ", p1 * p3)
|
55
exam/ex26/polynomial.py
Normal file
55
exam/ex26/polynomial.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
class Polynomial(object):
|
||||||
|
__slots__ = ["koefficients"]
|
||||||
|
|
||||||
|
def __init__(self, koefficients):
|
||||||
|
self.koefficients = list(koefficients)
|
||||||
|
# Remove trailing zeros.
|
||||||
|
while(self.koefficients and self.koefficients[-1] == 0):
|
||||||
|
self.koefficients.pop()
|
||||||
|
|
||||||
|
def __call__(self, x):
|
||||||
|
if(not isinstance(x, (float, int, complex))):
|
||||||
|
raise TypeError("unsupported type for {}: {}".format(x, type(x)))
|
||||||
|
|
||||||
|
result = 0
|
||||||
|
for i, a in enumerate(self.koefficients):
|
||||||
|
result += a * x**i
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
if(not isinstance(other, Polynomial)):
|
||||||
|
raise TypeError("cannot add {} and {}".format(type(self), type(other)))
|
||||||
|
|
||||||
|
p1 = self.koefficients
|
||||||
|
p2 = other.koefficients
|
||||||
|
if(len(other.koefficients) > len(self.koefficients)):
|
||||||
|
p1, p2 = p2, p1
|
||||||
|
|
||||||
|
p2 += [0] * (len(p1) - len(p2))
|
||||||
|
|
||||||
|
return Polynomial([a + b for a,b in zip(p1, p2)])
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
if(isinstance(other, (int, float, complex))):
|
||||||
|
return Polynomial([a * other for a in self.koefficients])
|
||||||
|
|
||||||
|
if(not isinstance(other, Polynomial)):
|
||||||
|
raise TypeError("cannot add {} and {}".format(type(self), type(other)))
|
||||||
|
|
||||||
|
result = defaultdict(int)
|
||||||
|
for i, k1 in enumerate(self.koefficients):
|
||||||
|
for j, k2 in enumerate(other.koefficients):
|
||||||
|
result[i + j] += k1 * k2
|
||||||
|
|
||||||
|
|
||||||
|
return Polynomial(result[i] for i in range(max(result)))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "{}({})".format(type(self).__name__, self.koefficients)
|
||||||
|
def __str__(self):
|
||||||
|
return " + ".join(["{}*x**{}".format(a, i) for i, a in enumerate(self.koefficients)])
|
||||||
|
|
||||||
|
|
101
exam/ex27/main.py
Normal file
101
exam/ex27/main.py
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
import sys
|
||||||
|
sys.path.append("../../")
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from collections import deque
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from util.io import readvalue
|
||||||
|
|
||||||
|
def positive_int(s):
|
||||||
|
i = int(s)
|
||||||
|
if(not i > 0):
|
||||||
|
raise ValueError("{} <= 0".format(i))
|
||||||
|
return i
|
||||||
|
def float_0_1(s):
|
||||||
|
i = float(s)
|
||||||
|
if(not i > 0):
|
||||||
|
raise ValueError("{} <= 0".format(i))
|
||||||
|
if(i > 1):
|
||||||
|
raise ValueError("{} > 1".format(i))
|
||||||
|
return i
|
||||||
|
|
||||||
|
IS_TREE = 1
|
||||||
|
TREE_IS_ON_FIRE = 2
|
||||||
|
TREE_WILL_BE_ON_FIRE = 4
|
||||||
|
|
||||||
|
def prepare_model(N, p):
|
||||||
|
model = np.zeros((N, N), dtype=np.int8)
|
||||||
|
model[np.random.uniform(size=(N,N)) < p] = IS_TREE
|
||||||
|
return model
|
||||||
|
|
||||||
|
def index_plus(i, j, add_i, add_j, model):
|
||||||
|
new_i = i + add_i
|
||||||
|
new_j = j + add_j
|
||||||
|
|
||||||
|
if(new_i >= model.shape[0]):
|
||||||
|
new_i -= model.shape[0]
|
||||||
|
if(new_i < 0):
|
||||||
|
new_i += model.shape[0]
|
||||||
|
if(new_j >= model.shape[1]):
|
||||||
|
new_j -= model.shape[1]
|
||||||
|
if(new_j < 0):
|
||||||
|
new_j += model.shape[1]
|
||||||
|
|
||||||
|
return new_i, new_j
|
||||||
|
|
||||||
|
|
||||||
|
def do_tick(model):
|
||||||
|
for i in range(model.shape[0]):
|
||||||
|
for j in range(model.shape[1]):
|
||||||
|
if((not model[i][j] & IS_TREE) or model[i][j] & TREE_IS_ON_FIRE):
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
if(
|
||||||
|
(model[index_plus(i, j, 1, 0, model)] & TREE_IS_ON_FIRE)
|
||||||
|
or (model[index_plus(i, j, 0, 1, model)] & TREE_IS_ON_FIRE)
|
||||||
|
or (model[index_plus(i, j, -1, 0, model)] & TREE_IS_ON_FIRE)
|
||||||
|
or (model[index_plus(i, j, 0, -1, model)] & TREE_IS_ON_FIRE)
|
||||||
|
):
|
||||||
|
|
||||||
|
model[i][j] |= TREE_WILL_BE_ON_FIRE
|
||||||
|
|
||||||
|
model[(model & TREE_WILL_BE_ON_FIRE) != 0] |= TREE_IS_ON_FIRE
|
||||||
|
model[(model & TREE_WILL_BE_ON_FIRE) != 0] ^= TREE_WILL_BE_ON_FIRE
|
||||||
|
|
||||||
|
def trees_on_fire(model):
|
||||||
|
unique, counts = np.unique((model & TREE_IS_ON_FIRE) != 0, return_counts=True)
|
||||||
|
if(True not in unique):
|
||||||
|
return 0
|
||||||
|
return counts[np.where(unique == True)[0]][0]
|
||||||
|
|
||||||
|
def set_initial_on_fire(model):
|
||||||
|
i = np.random.randint(0, model.shape[0])
|
||||||
|
j = np.random.randint(0, model.shape[1])
|
||||||
|
while True:
|
||||||
|
if(model[i][j] & IS_TREE):
|
||||||
|
model[i][j] |= TREE_IS_ON_FIRE
|
||||||
|
break
|
||||||
|
i = (i + 1) % model.shape[0]
|
||||||
|
j = (j + 1) % model.shape[1]
|
||||||
|
|
||||||
|
|
||||||
|
N = readvalue("N > ", positive_int)
|
||||||
|
p = readvalue("p > ", float_0_1)
|
||||||
|
|
||||||
|
model = prepare_model(N, p)
|
||||||
|
set_initial_on_fire(model)
|
||||||
|
|
||||||
|
on_fire = deque()
|
||||||
|
last_on_fire = 1
|
||||||
|
while True:
|
||||||
|
do_tick(model)
|
||||||
|
now_on_fire = trees_on_fire(model)
|
||||||
|
on_fire.append(now_on_fire)
|
||||||
|
if(now_on_fire == last_on_fire):
|
||||||
|
break
|
||||||
|
last_on_fire = now_on_fire
|
||||||
|
|
||||||
|
plt.plot(*zip(*enumerate(on_fire)))
|
||||||
|
plt.show()
|
Loading…
Reference in New Issue
Block a user