source: Board.py

Last change on this file was 5ee3950, checked in by Agnibho Mondal <mail@…>, 4 years ago

Added licensing information

  • Property mode set to 100644
File size: 10.4 KB
Line 
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
19from Tkinter import BooleanVar, StringVar
20import Tkinter as tk
21import tkFileDialog
22import tkMessageBox
23import Cell
24 
25class 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)
Note: See TracBrowser for help on using the repository browser.