This repository has been archived by the owner on Mar 5, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplayground.py
287 lines (229 loc) · 9.71 KB
/
playground.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
from cell import Cell
from tkinter import messagebox
import time
import fileTools
class Playground:
"""
The playground for the program. All cells are stored here. This object also import/export cells to the playground
:param screen: The screen object.
:param mouse: The mouse object.
:param keyboard: The keyboard object.
:param root: The root object.
Attributes:
cells: All the cells that is on the playground.
clickSwitch: The size of the one grid box in pixels.
"""
def __init__(self, root, screen, mouse, keyboard):
self.root = root
self.screen = screen
self.mouse = mouse
self.keyboard = keyboard
self.cells = []
self.clickSwitch = False
self.autoGenerateMode = False
self.generation = 0
self.timeToCalcGeneration = 0
self.bindKeyboardKeysToFunctions()
def bindKeyboardKeysToFunctions(self):
"""
Binds diffrent functions to keyboard presses.
:return: (nothing)
"""
self.keyboard.bindFunctionToKey("space", self.nextGeneration)
def updatePlayground(self):
"""
Updates the playground. Checking for user input to interact with the playground.
:return: (nothing)
"""
self.getMouseInput()
if(self.autoGenerateMode):
self.nextGeneration()
def getMouseInput(self):
"""
This method is getting the mouse and doing diffrent thing with it. For example: spawning a new cell if the user click on an grid-box.
:return: (nothing)
"""
xPos = self.mouse.xGridPos
yPos = self.mouse.yGridPos
#Changing the hoverblock color depending if the mouse is hovering over an living cell or not.
if(self.getCellFromPosition(xPos, yPos)):
self.screen.canvas.itemconfig(self.screen.hoverBlock, fill='#ff0000')
else:
self.screen.canvas.itemconfig(self.screen.hoverBlock, fill='#00ff00')
#Placing an cell on the playground if the user is clicking on the playground
if(self.mouse.leftButton and self.clickSwitch == False):
if(self.keyboard.shiftKey):
clickedCell = self.getCellFromPosition(xPos, yPos)
if(clickedCell == False):
self.createCell(xPos, yPos)
else:
self.deleteCell(clickedCell)
self.clickSwitch = True
if (self.mouse.leftButton == False and self.clickSwitch == True):
self.clickSwitch = False
def deleteCell(self, cell):
"""
Deleting a cell from the cell-list.
:param cell: The cell that is going to be delete.
:return: (nothing)
"""
index = self.cells.index(cell)
self.cells[index].delete()
self.cells.remove(cell)
def createCell(self, xPos, yPos):
"""
Creates a new cell for a given position.
:param xPos: The x-position on the grid.
:param yPos: the y-position on the grid
:return: (nothing)
"""
self.cells.append(Cell(self.screen, xPos, yPos))
def getCellFromPosition(self, xPos, yPos):
"""
Gets a cell from a given position.
:param xPos: The x-position on the grid.
:param yPos: the y-position on the grid
:return: Cell
"""
for cell in self.cells:
if(xPos == cell.x and yPos == cell.y):
return cell
return False
def clearPlayground(self):
"""
Removes all the cells from the playground
:return: (nothing)
"""
for cell in self.cells:
cell.delete()
self.cells = []
self.generation = 0
def importPlayground(self, filepath):
"""
This function is importing a playground.
:param filepath: The filepath to import the playground to.
:return: (nothing)
"""
cellOutOfBound = False
avgXPos = 0
avgYPos = 0
fileWrite = open(filepath, "r")
cellPositions = fileWrite.readlines()
self.clearPlayground()
for cellPos in cellPositions:
#Cleans the string
cleanCellPos = fileTools.cleanString(cellPos)
if(cleanCellPos == ""):
continue
#Check the format.
cleanCellPos = self.checkFileFormat(cleanCellPos)
if(cleanCellPos):
cellXPos, cellYPos = cleanCellPos
else:
return
#Checks if the coords is outside the world.
if(cellXPos > self.screen.worldSize or cellYPos > self.screen.worldSize or cellXPos < 0 or cellYPos < 0):
cellOutOfBound = True
else:
newCell = Cell(self.screen, cellXPos, cellYPos)
rectCellPos = self.screen.canvas.coords(newCell.rect)
avgXPos += rectCellPos[0]; avgYPos += rectCellPos[1]
self.cells.append(newCell)
#Print warning that some cells are not renderd.
if(cellOutOfBound):
messagebox.showwarning("Warning!", "Some cells are placed outside of the playground!")
#Moving the user to where the cells are.
avgXPos /= len(cellPositions); avgYPos /= len(cellPositions)
self.screen.offsetX += avgXPos - self.screen.width/2
self.screen.offsetY += avgYPos - self.screen.height/2
def exportPlayground(self, filepath):
"""
This function is exporting a playground.
:param filepath: The filepath to export the playground to.
:return: (nothing)
"""
cellPositions = ""
for cell in self.cells:
if(cell.dead == False):
cellPositions += str(cell.x) + " " + str(cell.y) + "\n"
fileWrite = open(filepath, "w")
fileWrite.write(cellPositions)
fileWrite.close()
def checkFileFormat(self, cellPos):
"""
Checks if the file has the right format for this program.
:param fileContent: The content of the file
:return: The positions in a tuple, (x, y), false if there is an error.
"""
try:
cellPosList = cellPos.split()
cellXPos = int(cellPosList[0])
cellYPos = int(cellPosList[1])
except ValueError:
messagebox.showerror("Error: Wrong format", "The choosen file do not have the correct format. Be so kind to choose an other file.")
return False
pass
return (cellXPos, cellYPos)
def removeCells(self, cellArray):
"""
Deletes all the cells from the array and playground.
:param cellArray: The array of cells to delete.
:return: (nothing)
"""
for cell in cellArray:
cell.delete()
self.cells.remove(cell)
def setNeighbors(self):
"""
Creates dead cells around all living cells and calculating all the neighbors for the dead and the living cells
:return: (nothing)
"""
for cellIndex in range(len(self.cells)):
cell = self.cells[cellIndex]
#Checks the 8 cells around the living one.
for neighborsX in range(cell.x - 1, cell.x + 2):
for neighborsY in range(cell.y - 1, cell.y + 2):
#If the position is outside the world, loop around.
neighborsX = neighborsX % self.screen.worldSize
neighborsY = neighborsY % self.screen.worldSize
#Skipping itself. Becouse we do not want to calculate itself as a neighbor
if(neighborsX == cell.x and neighborsY == cell.y):
continue
else:
#Checks if a cell exist at neighborsX, neighborsY
cellToCheck = self.getCellFromPosition(neighborsX, neighborsY)
if(cellToCheck != False):
#Add one to the neighbor var if there already exist and cell for the given position.
cellToCheck.numOfNeighbor += 1
else:
#Creates a new cell if it do not exist any.
newCell = Cell(self.screen, neighborsX, neighborsY, True)
newCell.numOfNeighbor += 1
self.cells.append(newCell)
def checkAmountOfNeighbors(self):
"""
Check the amount of neighbors and kills or creates new cell depending on the amount.
:return: (nothing)
"""
cellsToDelete = []
for cell in self.cells:
if(cell.numOfNeighbor > 3 or cell.numOfNeighbor < 2 or (cell.numOfNeighbor == 2 and cell.dead == True)):
cellsToDelete.append(cell)
elif(cell.numOfNeighbor == 3 and cell.dead == True):
cell.makeAlive()
cell.numOfNeighbor = 0
self.removeCells(cellsToDelete)
def nextGeneration(self):
"""
This method is updating the cells to the next generation.
:return: (nothing)
Thanks to Martins for the idea to have modolu of the current posotion.
"""
# Start a timer to calculate the time the render one generation.
startTime = int(round(time.time() * 100000))
self.generation += 1
self.setNeighbors()
self.checkAmountOfNeighbors()
# Ends a timer to calculate the time the render one generation.
endTime = int(round(time.time() * 100000))
self.timeToCalcGeneration = (endTime - startTime)