-
Notifications
You must be signed in to change notification settings - Fork 3
/
galtonboard.py
executable file
·124 lines (93 loc) · 3.53 KB
/
galtonboard.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import math
from random import choice
import time
GREEN = "\x1B[94m"
RESET = "\x1B[0m"
class GaltonBoard(object):
"""
Implements the logic of a Galton Board.
No UI is provided in this class,
instead 3 functions are provided as arguments to init
which are called by the class whenever anything happens which
requires a UI update.
"""
def __init__(self, rowcount = 7, ballcount = 40, on_init = None, on_ball_moved = None, on_total_changed = None):
"""
Simply set the attributes of the class from init arguments or to defaults
"""
self.board = []
self.rowcount = rowcount
self.ballcount = ballcount
self.totals = []
self.gridrows = 0
self.gridcolumns = 0
self.ballx = 0
self.bally = 0
self.prevballx = 0
self.prevbally = 0
self.pause_ms = 100
self.on_init = on_init
self.on_ball_moved = on_ball_moved
self.on_total_changed = on_total_changed
def initialize(self):
"""
Initializes the data structure to represent the Galton board
"""
# need to allow for spaces between pegs and a larger space above the pegs for balls to drop
self.gridrows = ((self.rowcount * 2) - 1) + 3
self.gridcolumns = (self.rowcount * 2) + 1
rowpegcount = 1
pegsdrawn = 0
# this centres the top peg horixontally
firstpegx = math.floor(self.gridcolumns / 2)
pegx = firstpegx
pegy = 3
# create a list for totals of the necessary size
self.totals = [0] * (self.rowcount + 1)
self.prevballx = -1
self.prevbally = -1
# create 2D array of pegs using letter 'O' to indicate a peg
for r in range(0, self.gridrows):
self.board.append(['*'] * self.gridcolumns)
for c in range(0, self.gridcolumns):
if r == pegy and c == pegx and pegsdrawn < rowpegcount:
self.board[r][c] = 'O'
pegsdrawn+=1
pegx+= 2
else:
self.board[r][c] = ' '
if r > 2 and (r%2) == 0:
rowpegcount+=1
pegsdrawn = 0
firstpegx-=1
pegx = firstpegx
pegy+= 2
# don't forget to call the function to tell the UI to draw the new board
if self.on_init:
self.on_init(self)
def start(self):
"""
Sets the board running in a big loop for the specified number of balls
"""
for b in range(1, self.ballcount + 1):
# set ball horizontal position to centre
self.ballx = math.floor(self.gridcolumns / 2)
for r in range(0, self.gridrows):
self.bally = r
# if we hit a peg move left or right
if self.board[r][self.ballx] == 'O':
self.ballx += choice([-1, 1])
if self.on_ball_moved:
self.on_ball_moved(self)
self.prevballx = self.ballx
self.prevbally = self.bally
if r < (self.gridrows - 1):
time.sleep(self.pause_ms/1000)
# calculate index of totals for current ball position
if self.ballx == 0:
totalindex = 0
else:
totalindex = int(self.ballx / 2)
self.totals[totalindex] += 1
if self.on_total_changed:
self.on_total_changed(self, totalindex, b);