]> Softwares of Agnibho - life.git/blob - Board.py
Added licensing information
[life.git] / Board.py
1 # Agnibho's Game of Life - Python implementation of Conway's Game of Life
2 # Copyright (C) 2014 Agnibho Mondal
3
4 # This file is part of Agnibho's Game of Life
5
6 # Agnibho's Game of Life is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10
11 # Agnibho's Game of Life is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with Agnibho's Game of Life. If not, see <http://www.gnu.org/licenses/>.
18
19 from Tkinter import BooleanVar, StringVar
20 import Tkinter as tk
21 import tkFileDialog
22 import tkMessageBox
23 import Cell
24
25 class Board(tk.Frame):
26 TIME=1000
27 cells=[]
28 SIZE=10
29 WIDTH=50
30 DEPTH=50
31 start_dim=[]
32 Num=0
33 Pop=-1
34 Max=0
35 mode="edit"
36 timer={}
37 original={}
38
39 def __init__(self, master=None):
40 tk.Frame.__init__(self, master)
41 self.grid()
42 self.stepnum=StringVar()
43 self.steppop=StringVar()
44
45 def render(self, width, depth):
46 self.WIDTH=width
47 self.DEPTH=depth
48 self.start_dim=[width, depth]
49 self.statusbar=tk.Frame(self)
50 self.steplbl=tk.Label(self.statusbar, textvariable=self.stepnum, padx=10)
51 self.poplbl=tk.Label(self.statusbar, textvariable=self.steppop, padx=10)
52 self.contain=tk.Canvas(self, width=self.WIDTH*self.SIZE, height=self.DEPTH*self.SIZE)
53 self.toolbar=tk.Frame(self)
54 self.startbtn=tk.Button(self.toolbar, text="Start", command=self.auto)
55 self.pausebtn=tk.Button(self.toolbar, text="Pause", state="disabled", command=self.pause)
56 self.stepbtn=tk.Button(self.toolbar, text="Next Step", command=self.step)
57 self.resetbtn=tk.Button(self.toolbar, text="Reset", command=self.reset)
58 self.revertbtn=tk.Button(self.toolbar, text="Revert", state="disabled", command=self.revert)
59 self.openbtn=tk.Button(self.toolbar, text="Open", command=self.openfile)
60 self.savebtn=tk.Button(self.toolbar, text="Save", command=self.savefile)
61 self.aboutbtn=tk.Button(self.toolbar, text="About", command=self.about)
62 self.slider=tk.Scale(self.toolbar, orient="horizontal", command=self.speed, from_=0, to=900, resolution=100, showvalue=0, length=150)
63 self.spdlbl1=tk.Label(self.toolbar, text="Slow")
64 self.spdlbl2=tk.Label(self.toolbar, text="Fast")
65 self.statusbar.grid(row=0)
66 self.steplbl.grid(row=0, column=0)
67 self.poplbl.grid(row=0, column=1)
68 self.contain.grid(row=1)
69 self.toolbar.grid(row=2)
70 self.startbtn.grid(row=0, column=0)
71 self.pausebtn.grid(row=0, column=1)
72 self.stepbtn.grid(row=0, column=2)
73 self.resetbtn.grid(row=0, column=3)
74 self.openbtn.grid(row=1, column=0)
75 self.savebtn.grid(row=1, column=1)
76 self.revertbtn.grid(row=1, column=2)
77 self.aboutbtn.grid(row=1, column=3)
78 self.spdlbl1.grid(row=2, column=0)
79 self.slider.grid(row=2, column=1, columnspan=2)
80 self.spdlbl2.grid(row=2, column=3)
81
82 for i in range(0, self.WIDTH):
83 self.cells.append([])
84 for j in range (0, self.DEPTH):
85 self.cells[i].append(Cell.Cell(self.contain.create_rectangle(i*self.SIZE, j*self.SIZE, i*self.SIZE+self.SIZE, j*self.SIZE+self.SIZE, fill="white", outline="gray"), self.contain))
86
87 def recreate(self, width, depth):
88 self.contain.destroy()
89 self.cells=[]
90 self.WIDTH=width
91 self.DEPTH=depth
92 if(self.SIZE*self.WIDTH>self.winfo_screenwidth() or self.SIZE*self.DEPTH>self.winfo_screenheight()-150):
93 self.SIZE=5
94 self.WIDTH=self.winfo_screenwidth()/self.SIZE
95 self.DEPTH=(self.winfo_screenheight()-150)/self.SIZE
96 else:
97 self.SIZE=10
98 self.contain=tk.Canvas(self, width=self.WIDTH*self.SIZE, height=self.DEPTH*self.SIZE)
99 self.contain.grid(row=1)
100
101 for i in range(0, self.WIDTH):
102 self.cells.append([])
103 for j in range (0, self.DEPTH):
104 self.cells[i].append(Cell.Cell(self.contain.create_rectangle(i*self.SIZE, j*self.SIZE, i*self.SIZE+self.SIZE, j*self.SIZE+self.SIZE, fill="white", outline="gray"), self.contain))
105
106 def run(self):
107 for i in range(0, self.WIDTH):
108 for j in range (0, self.DEPTH):
109 self.cells[i][j].freeze()
110 self.original=self.cells
111 self.mode="man"
112 self.revertbtn["state"]="normal"
113
114 def step(self):
115 if(self.Pop==0):
116 tkMessageBox.showwarning("Game over", "All life forms have been terminated")
117 return
118 if(self.mode=="edit"):
119 self.run()
120 for i in range(0, self.WIDTH):
121 for j in range (0, self.DEPTH):
122 z=self.count(i, j)
123 if(self.cells[i][j].getState()=="live"):
124 if(z<2 or z>3):
125 self.cells[i][j].makeDead()
126 elif(self.cells[i][j].getState()=="dead"):
127 if(z==3):
128 self.cells[i][j].makeLive()
129 self.Pop=0
130 for i in range(0, self.WIDTH):
131 for j in range (0, self.DEPTH):
132 self.cells[i][j].commit()
133 if(self.cells[i][j].getState()=="live"):
134 self.Pop+=1
135 if(self.Pop>self.Max):
136 self.Max=self.Pop
137 if(self.Pop>0):
138 self.Num+=1
139 else:
140 tkMessageBox.showwarning("Game over", "All life forms have been terminated\nMaximun polpulation reached: "+str(self.Max))
141 self.pause()
142 self.startbtn["state"]="disabled"
143 self.stepbtn["state"]="disabled"
144 if(self.WIDTH>30):
145 self.stepnum.set("Generation: "+str(self.Num))
146 self.steppop.set("Population: "+str(self.Pop))
147 else:
148 self.stepnum.set(str("Gen:"+str(self.Num)))
149 self.steppop.set("Pop:"+str(self.Pop))
150 if(self.mode=="auto"):
151 self.timer=self.after(self.TIME, self.step)
152
153 def auto(self):
154 self.startbtn["state"]="disabled"
155 self.stepbtn["state"]="disabled"
156 self.pausebtn["state"]="normal"
157 if(self.mode=="edit"):
158 self.run()
159 self.mode="auto"
160 self.timer=self.after(self.TIME, self.step)
161
162 def pause(self):
163 self.startbtn["state"]="normal"
164 self.stepbtn["state"]="normal"
165 self.pausebtn["state"]="disabled"
166 self.mode="man"
167 self.after_cancel(self.timer)
168
169 def openfile(self):
170 filename=tkFileDialog.askopenfilename(filetypes=[("Life 1.06", "*.life"), ("Life 1.6", "*.lif"), ("All", "*")])
171 self.parsefile(filename)
172
173 def savefile(self):
174 filename=tkFileDialog.asksaveasfilename(filetypes=[("Life 1.06", "*.life"), ("Life 1.6", "*.lif")], defaultextension=".life")
175 self.writefile(filename)
176
177 def parsefile(self, filename):
178 self.reset()
179 arr=[]
180 arr.append([])
181 arr.append([])
182 if(len(filename)>0):
183 try:
184 with open(filename) as f:
185 for line in f:
186 data=line.strip().lower()
187 if(len(data)>0):
188 if(data.startswith("#life")):
189 if(data!="#life 1.06" and data!="#life 1.6"):
190 tkMessageBox.showerror("Format Error", "The file must be in Life 1.06 format.")
191 return
192 elif(data.startswith("#")):
193 continue
194 else:
195 arr[0].append([])
196 arr[1].append([])
197 try:
198 arr[0][-1]=int(data.split()[0])
199 arr[1][-1]=int(data.split()[1])
200 except ValueError:
201 tkMessageBox.showerror("Format Error", "Failed to open. The file has formatting error.")
202 print "Failed to open file\nFormat error near: "+line
203 return
204 except IOError:
205 tkMessageBox.showerror("File not found", "Failed to open the specified file. Make sure the file exists and is readable.")
206 return
207 except Exception, e:
208 tkMessageBox.showerror("Error", "Failed to open the specified file.")
209 print e
210 return
211 else:
212 return
213 if((max(arr[0])-min(arr[0]))>self.WIDTH/2 or (max(arr[1])-min(arr[1]))>self.DEPTH/2):
214 if((max(arr[0])-min(arr[0]))>self.WIDTH/2):
215 width=(max(arr[0])-min(arr[0]))*2
216 depth=self.DEPTH
217 if((max(arr[1])-min(arr[1]))>self.DEPTH/2):
218 depth=(max(arr[1])-min(arr[1]))*2
219 width=self.WIDTH
220 self.recreate(width, depth)
221
222 try:
223 if(min(arr[0])<=0):
224 for i in range(0, len(arr[0])):
225 arr[0][i]+=self.WIDTH/2
226 if(min(arr[1])<=0):
227 for i in range(0, len(arr[1])):
228 arr[1][i]+=self.DEPTH/2
229 for i in range(0, len(arr[0])):
230 while(arr[0][i]<0):
231 arr[0][i]+=self.WIDTH
232 while(arr[0][i]>self.WIDTH):
233 arr[0][i]-=self.WIDTH
234 for i in range(0, len(arr[0])):
235 while(arr[1][i]<0):
236 arr[1][i]+=self.DEPTH
237 while(arr[1][i]>self.DEPTH):
238 arr[1][i]-=self.DEPTH
239 for i in range(0, len(arr[0])):
240 self.cells[arr[0][i]][arr[1][i]].toggle()
241 except Exception, e:
242 tkMessageBox.showerror("Error", "Something went wrong. Failed to open the file properly.")
243 print e
244 self.reset(True, True)
245
246 def writefile(self, filename):
247 if(len(filename)>0):
248 f=open(filename, "w")
249 f.write("#Life 1.06\r\n")
250 for i in range(0, self.WIDTH):
251 for j in range (0, self.DEPTH):
252 if(self.cells[i][j].getState()=="live"):
253 f.write(str(i)+" "+str(j)+"\r\n")
254 f.close()
255
256 def reset(self, full=True, force=False):
257 self.Pop=0
258 for i in range(0, self.WIDTH):
259 for j in range (0, self.DEPTH):
260 if(self.cells[i][j].getState()=="live"):
261 self.Pop+=1
262 if(force):
263 flag=True
264 elif(self.Pop>0):
265 flag=tkMessageBox.askokcancel("Confirm Reset", "This action will erase all the cells from current board. Are you sure to continue?")
266 else:
267 flag=True
268 if(flag):
269 self.pause()
270 self.mode="edit"
271 self.Num=0
272 self.Pop=-1
273 self.Max=0
274 self.stepnum.set("")
275 self.steppop.set("")
276 if(full):
277 if(self.WIDTH==self.start_dim[0] and self.DEPTH==self.start_dim[1]):
278 for i in range(0, self.WIDTH):
279 for j in range (0, self.DEPTH):
280 self.cells[i][j].clear()
281 else:
282 self.recreate(self.start_dim[0], self.start_dim[1])
283 self.revertbtn["state"]="disabled"
284
285 def revert(self):
286 self.reset(False)
287 for i in range(0, self.WIDTH):
288 for j in range (0, self.DEPTH):
289 self.cells[i][j].revert()
290
291 def about(self):
292 tkMessageBox.showinfo("About Life", "Life 1.04 by Agnibho Mondal\nBased on Conway's Game of Life\nhttp://code.agnibho.com/life")
293
294 def count(self, i, j):
295 z=0
296 for cl in [self.getCell(i-1, j-1), self.getCell(i-1, j), self.getCell(i, j-1), self.getCell(i+1, j+1), self.getCell(i+1, j), self.getCell(i, j+1), self.getCell(i-1, j+1), self.getCell(i+1, j-1)]:
297 if(cl.getState()=="live"):
298 z+=1
299 return z
300
301 def getCell(self, x, y):
302 if(x==-1):
303 x=self.WIDTH-1
304 elif(x>=self.WIDTH):
305 x=0
306 if(y==-1):
307 y=self.DEPTH-1
308 elif(y>=self.DEPTH):
309 y=0
310 return self.cells[x][y]
311
312 def speed(self, val):
313 self.TIME=1000-int(val)