Shouldn’t field
be undefined on line 50? It was my understanding that inner nested classes did not have visibility to outer classes, as I ran into on line 65… Just seems kind of inconsistent and I would love to have a better understanding of the outer nested classes visibility to avoid any confusion in the future.
JavaScript
x
201
201
1
"""######################################### Imports ############################################"""
2
import turtle
3
import time
4
import functools
5
"""######################################### Key press ##########################################"""
6
7
def key_space(): # Resets Level
8
field.setLevel()
9
10
def key_p(): # Skips Level
11
field.nextLevel()
12
13
def key_k(): # For making levels
14
print([(b.x,b.y) for b in field.getBoxes()])
15
16
"""######################################## Interface(s) ########################################"""
17
18
def stamp(self): # Interface for drawing polygons.
19
turtle.goto(self.canvasX+field.offset[0],self.canvasY+field.offset[1])
20
turtle.shape(self.shape)
21
turtle.color(self.color)
22
turtle.stamp()
23
24
def frameBoiler(func): #
25
@functools.wraps(func)
26
def wrapper_frame_boiler(*args,**kwargs):
27
turtle.clear()
28
#turtle.getscreen().bgpic('Untitled.png')
29
r = func(*args, **kwargs)
30
turtle.update()
31
time.sleep(0.05)
32
return r
33
return wrapper_frame_boiler
34
35
"""######################################### Game Logic #########################################"""
36
37
class field: # Field is an abstract class which facilitates the game Mechanics
38
def place(o): # Place translates an objects board position to a pixel or canvas position
39
o.canvasX = (o.x-31)*field.spaceSize # 31 beacuse it's half of the magic number...
40
o.canvasY = (o.y-31)*field.spaceSize
41
return(o) # This method should be a void //TODO refactor...
42
class square:
43
padding = 2
44
color = "#078900"
45
shape = "sq" # "sq" is the name of the stamp polygon geometry.
46
#//TODO refactor: eliminate redundant literals. see def turtleSetup
47
def __init__(self,x,y):
48
self.x = x
49
self.y = y
50
field.place(self)
51
def draw(self):
52
stamp(self)
53
class level: # Levels are what make the game fun.
54
class mando: # These are the spots you have to populate
55
thickness = 2 #
56
trim = 1
57
color = "#ee0000"
58
shape = "bx" # "bx" is the name of the stamp polygon geometry.
59
#//TODO refactor: eliminate redundant literals. see def turtleSetup
60
def draw(self): # each mando needs to be visable...
61
stamp(self) # line: 15: `def stamp(self)`
62
def __init__(self,x,y): # make a mandatory space
63
self.x = x
64
self.y = y
65
self.canvasX = 0 # field isn't visible from this deeply nested class
66
self.canvasY = 0 # so these are "unplaced"
67
def __init__(self,text,spots): # make a level
68
self.text = text
69
self.mandos = []
70
self.complete = False
71
for spot in spots:
72
self.mandos.append(self.mando(spot[0],spot[1]))
73
def checkWin(self): # If the boxes on the board match the mandos you beat the level.
74
oneLiner = set([(b.x,b.y) for b in field.getBoxes()])
75
isTooLong = set([(m.x,m.y) for m in self.mandos])
76
self.complete = oneLiner == isTooLong
77
def draw(self): # This draws the mandos and the level's acompanying text.
78
for mando in self.mandos:
79
mando.draw()
80
if self.complete:
81
field.nextLevel()
82
spaces = [[None]*63 for k in range(0,63)] # 63 is the magic number, ralistically it could be 8
83
levels =[level("letsGo",[(31,33),(31,29),(29,31),(33,31)]),
84
level("NevernEatnShreadednWheat",[(27, 31), (28, 30), (28, 32), (29, 30), (29, 32),
85
(30, 28), (30, 29), (30, 33), (30, 34), (31, 27), (31, 35), (32, 28), (32, 29),
86
(32, 33), (32, 34), (33, 30), (33, 32), (34, 30), (34, 32), (35, 31)]),
87
level("Try:nPress Space",[(29, 31), (30, 30), (31, 31), (31, 33), (32, 29), (33, 32),
88
(34, 31)]),
89
level("Flex",[(28, 27), (28, 28), (28, 29), (29, 26), (29, 30), (29, 35), (29, 36),
90
(30, 29), (30, 31), (30, 33), (30, 34), (30, 35), (30, 37), (31, 28), (31, 29),
91
(31, 33), (31, 35), (31, 36), (32, 29), (32, 31), (32, 33), (32, 34), (32, 35),
92
(32, 37), (33, 26), (33, 30), (33, 35), (33, 36), (34, 27), (34, 28), (34, 29)]),
93
level("Blast Off",[(28, 28), (28, 29), (28, 31), (29, 27), (29, 31), (29, 32), (30, 28),
94
(30, 29), (30, 31), (30, 32), (30, 33), (30, 34), (31, 33), (31, 35), (32, 28),
95
(32, 29), (32, 31), (32, 32), (32, 33), (32, 34), (33, 27), (33, 31), (33, 32),
96
(34, 28), (34, 29), (34, 31)]),
97
level("SpacenInvaders",[(27, 31), (28, 30), (28, 32), (29, 27), (29, 28), (29, 30),
98
(29, 31), (30, 26), (30, 28), (30, 29), (30, 33), (31, 27), (31, 28), (31, 31),
99
(31, 32), (32, 26), (32, 28), (32, 29), (32, 33), (33, 27), (33, 28), (33, 30),
100
(33, 31), (34, 30), (34, 32), (35, 31)]),
101
level("big oof",[(31,31),(32,31),(31,33)])]
102
levelIndex = 0 # literally the number indicating what level you're on
103
spaceSize = 40 # the number of pixels to a in gmae space.
104
offset = [-80,0] # you can arbitrailly move the gmae around the screen...
105
def hit(x,y): # toggle the presence of a box in a space on the board/field.
106
try:
107
if field.spaces[x][y] is None:
108
field.spaces[x][y] = field.place(field.square(x,y))
109
else:
110
field.spaces[x][y] = None
111
except IndexError:
112
pass
113
def setLevel(): # clears the board puts a box in the middle and places the mandos.
114
field.spaces = [[None]*63 for k in range(0,63)]
115
field.hit(31,31)
116
[field.place(mando) for mando in field.levels[field.levelIndex].mandos]
117
def nextLevel(): # the first level is also the level after the last level.
118
field.levelIndex += 1
119
if field.levelIndex >= len(field.levels):
120
field.levelIndex = 0
121
field.setLevel()
122
@frameBoiler
123
def draw(): # this is the draw method for the
124
field.levels[field.levelIndex].draw()
125
for box in field.getBoxes():
126
box.draw()
127
turtle.color("#bad4af")# // Todo figure out why the text causes a flicker.
128
turtle.goto(field.levels[field.levelIndex].
129
mandos[-1].canvasX,field.levels[field.levelIndex].mandos[-1].canvasY)
130
turtle.write(field.levels[field.levelIndex].text,
131
font=('Courier', 33, 'italic'), align='left')
132
def click(x,y):
133
spacex = int((float(x)-field.offset[0])/field.spaceSize +31) # more magic numbers...
134
spacey = int((float(y)-field.offset[1])/field.spaceSize +32) # change them I dare you.
135
try:
136
field.bop(spacex,spacey)
137
except IndexError:
138
pass
139
field.levels[field.levelIndex].checkWin()
140
def getBoxes(): # in reality field.spaces should just be a dictionary... // TODO
141
return sum([[box for box in boxes if not box is None] for boxes in field.spaces],[])
142
def bop(x,y):
143
if field.spaces[x][y] is None:
144
pass
145
else:
146
field.hit(x,y)
147
field.hit(x+1,y)
148
field.hit(x-1,y)
149
field.hit(x,y+1)
150
field.hit(x,y-1)
151
152
"""##############################################################################################"""
153
def turtleSetup():
154
turtle.tracer(0,0)
155
turtle.register_shape("sq", # This is a square that denotes the boxes... wait
156
((field.square.padding,
157
field.square.padding),
158
(field.spaceSize-field.square.padding,
159
field.square.padding),
160
(field.spaceSize-field.square.padding,
161
field.spaceSize-field.square.padding),
162
(field.square.padding,
163
field.spaceSize-field.square.padding)))
164
turtle.register_shape("bx", # this is a box that is used to denote the mandos... woops.
165
((field.level.mando.trim,field.level.mando.trim),
166
(field.level.mando.thickness,
167
field.level.mando.thickness),
168
(field.spaceSize-field.level.mando.thickness,
169
field.level.mando.thickness),
170
(field.spaceSize-field.level.mando.thickness,
171
field.spaceSize-field.level.mando.thickness),
172
(field.level.mando.thickness,
173
field.spaceSize-field.level.mando.thickness),
174
(field.level.mando.thickness,
175
field.level.mando.thickness),
176
(field.level.mando.trim,
177
field.level.mando.trim),
178
(field.level.mando.trim,
179
field.spaceSize-field.level.mando.trim),
180
(field.spaceSize-field.level.mando.trim,
181
field.spaceSize-field.level.mando.trim),
182
(field.spaceSize-field.level.mando.trim,
183
field.level.mando.trim)))
184
turtle.ht() # Hide Turtle so you don't get a indicator on the rendering mechanism
185
turtle.pu() # Pen up so you don't see the path of the rendering mechanism.
186
turtle.onkey(key_space, "space") # Register key events.
187
turtle.onkey(key_k, "k")
188
turtle.onkey(key_p, "q")
189
turtle.getscreen().onclick(field.click) # register the click event
190
turtle.listen() # this probably needs to be here for some reason, idk delete it & see for yaself
191
192
"""##############################################################################################"""
193
194
class main:
195
def __init__(self):
196
turtleSetup()
197
field.setLevel()
198
while None is None:
199
field.draw()
200
main() # Look at how clean that main function is!
201
Advertisement
Answer
I’ve write very simplified example:
JavaScript
1
12
12
1
class A:
2
class B:
3
def __init__(self):
4
print(A)
5
class C:
6
class D:
7
def __init__(self):
8
print(A)
9
A.B()
10
A.C.D()
11
A().C().D()
12
And class A
is available as in inner as in inner inner class.
By initializing the levels after defining the game field rather than initializing the levels in the game field definition we are able to avoid the unresolved name error. See modified code.
JavaScript
1
206
206
1
import turtle
2
import time
3
import functools
4
"""######################################### Key press ##########################################"""
5
6
def key_space(): # Resets Level
7
field.setLevel()
8
9
def key_p(): # Skips Level
10
field.nextLevel()
11
12
def key_k(): # For making levels
13
print([(b.x,b.y) for b in field.getBoxes()])
14
15
"""######################################## Interface(s) ########################################"""
16
17
def stamp(self): # Interface for drawing polygons.
18
turtle.goto(self.canvasX+field.offset[0],self.canvasY+field.offset[1])
19
turtle.shape(self.shape)
20
turtle.color(self.color)
21
turtle.stamp()
22
23
def frameBoiler(func): #
24
@functools.wraps(func)
25
def wrapper_frame_boiler(*args,**kwargs):
26
turtle.clear()
27
#turtle.getscreen().bgpic('Untitled.png')
28
r = func(*args, **kwargs)
29
turtle.update()
30
time.sleep(0.05)
31
return r
32
return wrapper_frame_boiler
33
34
"""######################################### Game Logic #########################################"""
35
36
class field: # Field is an abstract class which facilitates the game Mechanics
37
def place(o): # Place translates an objects board position to a pixel or canvas position
38
o.canvasX = (o.x-31)*field.spaceSize # 31 beacuse it's half of the magic number...
39
o.canvasY = (o.y-31)*field.spaceSize
40
return(o) # This method should be a void //TODO refactor...
41
class square:
42
padding = 2
43
color = "#078900"
44
shape = "sq" # "sq" is the name of the stamp polygon geometry.
45
#//TODO refactor: eliminate redundant literals. see def turtleSetup
46
def __init__(self,x,y):
47
self.x = x
48
self.y = y
49
field.place(self)
50
def draw(self):
51
stamp(self)
52
class level: # Levels are what make the game fun.
53
class mando: # These are the spots you have to populate
54
thickness = 2 #
55
trim = 1
56
color = "#ee0000"
57
shape = "bx" # "bx" is the name of the stamp polygon geometry.
58
#//TODO refactor: eliminate redundant literals. see def turtleSetup
59
def draw(self): # each mando needs to be visable...
60
stamp(self) # line: 15: `def stamp(self)`
61
def __init__(self,x,y): # make a mandatory space
62
self.x = x
63
self.y = y
64
field.place(self)
65
def __init__(self,text,spots): # make a level
66
self.text = text
67
self.mandos = []
68
self.complete = False
69
for spot in spots:
70
self.mandos.append(self.mando(spot[0],spot[1]))
71
def checkWin(self): # If the boxes on the board match the mandos you beat the level.
72
oneLiner = set([(b.x,b.y) for b in field.getBoxes()])
73
isTooLong = set([(m.x,m.y) for m in self.mandos])
74
self.complete = oneLiner == isTooLong
75
def draw(self): # This draws the mandos and the level's acompanying text.
76
for mando in self.mandos:
77
mando.draw()
78
if self.complete:
79
field.nextLevel()
80
spaces = [[None]*63 for k in range(0,63)] # 63 is the magic number, ralistically it could be 8
81
levelIndex = 0 # literally the number indicating what level you're on
82
spaceSize = 40 # the number of pixels to a in gmae space.
83
offset = [-80,0] # you can arbitrailly move the gmae around the screen...
84
def hit(x,y): # toggle the presence of a box in a space on the board/field.
85
try:
86
if field.spaces[x][y] is None:
87
field.spaces[x][y] = field.place(field.square(x,y))
88
else:
89
field.spaces[x][y] = None
90
except IndexError:
91
pass
92
def setLevel(): # clears the board puts a box in the middle and places the mandos.
93
field.spaces = [[None]*63 for k in range(0,63)]
94
field.hit(31,31)
95
[field.place(mando) for mando in field.levels[field.levelIndex].mandos]
96
def nextLevel(): # the first level is also the level after the last level.
97
field.levelIndex += 1
98
if field.levelIndex >= len(field.levels):
99
field.levelIndex = 0
100
field.setLevel()
101
@frameBoiler
102
def draw(): # this is the draw method for the
103
field.levels[field.levelIndex].draw()
104
for box in field.getBoxes():
105
box.draw()
106
turtle.color("#bad4af")# // Todo figure out why the text causes a flicker.
107
turtle.goto(field.levels[field.levelIndex].
108
mandos[-1].canvasX,field.levels[field.levelIndex].mandos[-1].canvasY)
109
turtle.write(field.levels[field.levelIndex].text,
110
font=('Courier', 33, 'italic'), align='left')
111
def click(x,y):
112
spacex = int((float(x)-field.offset[0])/field.spaceSize +31) # more magic numbers...
113
spacey = int((float(y)-field.offset[1])/field.spaceSize +32) # change them I dare you.
114
try:
115
field.bop(spacex,spacey)
116
except IndexError:
117
pass
118
field.levels[field.levelIndex].checkWin()
119
def getBoxes(): # in reality field.spaces should just be a dictionary... // TODO
120
return sum([[box for box in boxes if not box is None] for boxes in field.spaces],[])
121
def bop(x,y):
122
if field.spaces[x][y] is None:
123
pass
124
else:
125
field.hit(x,y)
126
field.hit(x+1,y)
127
field.hit(x-1,y)
128
field.hit(x,y+1)
129
field.hit(x,y-1)
130
131
132
"""################################## Initialize Leveles #########################################"""
133
134
field.levels =[field.level("Space For Resets",[(31,33),(31,29),(29,31),(33,31)]),
135
field.level("NevernEatnShreadednWheat",[(27, 31), (28, 30), (28, 32), (29, 30), (29, 32),
136
(30, 28), (30, 29), (30, 33), (30, 34), (31, 27), (31, 35), (32, 28), (32, 29),
137
(32, 33), (32, 34), (33, 30), (33, 32), (34, 30), (34, 32), (35, 31)]),
138
field.level("Ok?",[(27, 31), (31, 27), (31, 35), (35, 31)]),
139
field.level("Try:nPress Space",[(29, 31), (30, 30), (31, 31), (31, 33), (32, 29), (33, 32),
140
(34, 31)]),
141
field.level("Flex",[(28, 27), (28, 28), (28, 29), (29, 26), (29, 30), (29, 35), (29, 36),
142
(30, 29), (30, 31), (30, 33), (30, 34), (30, 35), (30, 37), (31, 28), (31, 29),
143
(31, 33), (31, 35), (31, 36), (32, 29), (32, 31), (32, 33), (32, 34), (32, 35),
144
(32, 37), (33, 26), (33, 30), (33, 35), (33, 36), (34, 27), (34, 28), (34, 29)]),
145
field.level("Blast Off",[(28, 28), (28, 29), (28, 31), (29, 27), (29, 31), (29, 32), (30, 28),
146
(30, 29), (30, 31), (30, 32), (30, 33), (30, 34), (31, 33), (31, 35), (32, 28),
147
(32, 29), (32, 31), (32, 32), (32, 33), (32, 34), (33, 27), (33, 31), (33, 32),
148
(34, 28), (34, 29), (34, 31)]),
149
field.level("SpacenInvaders",[(27, 31), (28, 30), (28, 32), (29, 27), (29, 28), (29, 30),
150
(29, 31), (30, 26), (30, 28), (30, 29), (30, 33), (31, 27), (31, 28), (31, 31),
151
(31, 32), (32, 26), (32, 28), (32, 29), (32, 33), (33, 27), (33, 28), (33, 30),
152
(33, 31), (34, 30), (34, 32), (35, 31)]),
153
field.level("big oof",[(31,31),(32,31),(31,33)])]
154
155
"""##############################################################################################"""
156
def turtleSetup():
157
turtle.tracer(0,0)
158
turtle.register_shape("sq", # This is a square that denotes the boxes... wait
159
((field.square.padding,
160
field.square.padding),
161
(field.spaceSize-field.square.padding,
162
field.square.padding),
163
(field.spaceSize-field.square.padding,
164
field.spaceSize-field.square.padding),
165
(field.square.padding,
166
field.spaceSize-field.square.padding)))
167
turtle.register_shape("bx", # this is a box that is used to denote the mandos... woops.
168
((field.level.mando.trim,field.level.mando.trim),
169
(field.level.mando.thickness,
170
field.level.mando.thickness),
171
(field.spaceSize-field.level.mando.thickness,
172
field.level.mando.thickness),
173
(field.spaceSize-field.level.mando.thickness,
174
field.spaceSize-field.level.mando.thickness),
175
(field.level.mando.thickness,
176
field.spaceSize-field.level.mando.thickness),
177
(field.level.mando.thickness,
178
field.level.mando.thickness),
179
(field.level.mando.trim,
180
field.level.mando.trim),
181
(field.level.mando.trim,
182
field.spaceSize-field.level.mando.trim),
183
(field.spaceSize-field.level.mando.trim,
184
field.spaceSize-field.level.mando.trim),
185
(field.spaceSize-field.level.mando.trim,
186
field.level.mando.trim)))
187
turtle.ht() # Hide Turtle so you don't get a indicator on the rendering mechanism
188
turtle.pu() # Pen up so you don't see the path of the rendering mechanism.
189
turtle.onkey(key_space, "space") # Register key events.
190
turtle.onkey(key_k, "k")
191
turtle.onkey(key_p, "q")
192
turtle.getscreen().onclick(field.click) # register the click event
193
turtle.listen() # this probably needs to be here for some reason, idk delete it & see for yaself
194
195
"""##############################################################################################"""
196
197
class main:
198
def __init__(self):
199
turtleSetup()
200
field.setLevel()
201
while None is None:
202
field.draw()
203
main() # Look at how clean that main function is! ```
204
205
206