-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathallwet.py
executable file
·823 lines (631 loc) · 29.9 KB
/
allwet.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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
#!/usr/local/bin/pythonw2.7-32
#Copyright 2015-2016 Andrew Eric Zane
import pygame
import sys
import math
from random import randrange
##Create iterable metaclass
class IterRegistry(type):
def __iter__(cls):
return iter(cls._registry)
class Segment(object):
def __init__(self, window, color, pressure, thickness, segNum, xStart, yStart, xEnd, yEnd):
##ARGS
self.window = window
self.color = color
self.pressure = pressure
self.rnge = self.pressure
self.segNum = segNum
self.xStart = xStart
self.yStart = yStart
self.xEnd = xEnd
self.yEnd = yEnd
self.thickness = thickness
##/ARGS
##INITS
self.forming = True
self.finished = False
##Theta for collision detection
self.theta = math.atan2((self.yEnd-self.yStart), (self.xEnd-self.xStart))
##All of the below additionally used by collision detection
self.Px, self.Py, self.xLen, self.yLen = self.get_Pxy()
self.Pl = self.get_Pl()
self.allowedLen = self.get_allowedLen()
##Init drawing coords
self.xCurStart = self.xStart
self.yCurStart = self.yStart
self.xCurEnd = self.xStart
self.yCurEnd = self.yStart
self.curLen = 0
self.traveledLen = 0
self.actualLen = 0
##/INITS
def get_Pxy(self, xStart = None, yStart = None, xEnd = None, yEnd = None, rnge = None):
##ARGS
if xStart is None:
xStart = self.xStart
if yStart is None:
yStart = self.yStart
if xEnd is None:
xEnd = self.xEnd
if yEnd is None:
yEnd = self.yEnd
if rnge is None:
rnge = self.rnge
##/ARGS
xLen = xEnd-xStart
yLen = yEnd-yStart
##This results in the traveling of a segment by a magnitude of 1*pressure per frame
##Also, P* MUST be normalized to a magnitude of 1 in order that collision detection works
##So, because of the origin of rnge, this function needs to be called every time the pressure changes
Px = xLen/rnge
Py = yLen/rnge
return Px, Py, xLen, yLen
def get_allowedLen(self, segNum = None, pressure = None):
##ARGS
if segNum is None:
segNum = self.segNum
if pressure is None:
pressure = self.pressure
##/ARGS
return pressure/segNum
def increment_seg(self):
"""Returns the proportional length additive-at-increment"""
##If we wanted dynamic pressure change, we could check allowedLen here
##print "Length traveled: " + str(self.traveledLen)
##print "Current endpoint: " + str(self.xCurEnd) + ", " + str(self.yCurEnd)
if self.traveledLen < self.rnge:
self.xCurEnd = self.xCurEnd + self.Px
self.yCurEnd = self.yCurEnd + self.Py
self.actualLen = self.actualLen + self.Pl
##Don't subtract from curLen when we stop incrementing *curEnd.
## curLen is only accurate until this point, otherwise, the below
## comparison would break, the *CurStart would not increment
## curLen must represent MAX segment length after formed.
## even when the segment length ceases to be added to.
##Inc traveledLen even after not adding for self.finished check
self.traveledLen = self.traveledLen + self.Pl
if self.curLen < self.allowedLen:
self.curLen = self.curLen + self.Pl
##Leave self.*CurStart as self.*Start
else:
self.xCurStart = self.xCurStart + self.Px
self.yCurStart = self.yCurStart + self.Py
self.actualLen = self.actualLen - self.Pl
##No longer forming, so head knows to start new segment
if self.forming:
self.forming = False
##Check finished, when
if (self.traveledLen - self.curLen) > self.rnge:
self.finished = True
return not self.finished
def get_Pl(self, Px = None, Py = None):
##ARGS
if Px is None:
Px = self.Px
if Py is None:
Py = self.Py
##/ARGS
return math.sqrt((Px**2)+(Py**2))
def reDraw(self):
##self.increment_segment is true if not finished
if self.increment_seg():
pygame.draw.line(self.window, self.color, (self.xCurStart, self.yCurStart), (self.xCurEnd, self.yCurEnd), self.thickness)
def runner_Xmove(self, distance):
self.xCurStart = self.xCurStart + distance
self.xCurEnd = self.xCurEnd + distance
##I don't think we need to increment anything beyond the current x's
self.xEnd = self.xEnd + distance
class Runner(object):
##FIXME
## Needs value error and type error exception handling
##/FIXME
##Class Elements
__diagMove = 0.707
##Link to metaclass, define registry
__metaclass__ = IterRegistry
_registry = []
def __init__(self, window, arrows, top, speed, image):
"""Speed is distance moved per frame
Image is a path here, note comment in reDraw()"""
##Append instance to class registry
self._registry.append(self)
self.window = window
self.top = top
self.defSpeed = speed
##Get x, y and dist = 0 from start
##self.speed declared here
self.x, self.y, self.distance = self.start()
##Init lastPlaced distance
self.lastPlaced = 0
##self.arrows is a bool true:arrow keys, false:wasd
self.arrows = arrows
##Convert image parameter into a pygame image, store as instance variable
self.image = pygame.image.load(image)
self.image = pygame.transform.scale(self.image, (15, 20))
def start(self, window=None, top=None):
if window is None:
window = self.window
if top is None:
top = self.top
##Tell sprinkler appender that we're ready to restart
self.reInit = True
x, y = window.get_size()
##Center vertically
##y = y/2
##OPPOSITE SIDES
##if top:
##Put at the left 25th of the screen
##x = x/25
##else:
##Put at the right 25th of the screen
##x = x - (x/25)
if top:
y=(y/2) - 15
else:
y=(y/2) + 15
x = x/3
self.speed = self.defSpeed
##Reset distance traveled
dist = 0
##Put cursor on guy for mouse control
pygame.mouse.set_pos([x, y])
return x, y, dist
##<KEY CONTROL>
def moveY(self, YPositive):
if not YPositive:
dirSpeed = self.speed * (-1)
else:
dirSpeed = self.speed
self.y = self.y + dirSpeed
def moveX(self, XPositive):
if not XPositive:
dirSpeed = self.speed * (-1)
else:
dirSpeed = self.speed
self.x = self.x + dirSpeed
def moveXSprink(self, XPositive):
if not XPositive:
dirSpeed = self.speed
else:
dirSpeed = self.speed * (-1)
for sprinkler in Rotator:
sprinkler.runner_Xmove(dirSpeed)
self.distance = self.distance - dirSpeed
def moveDiagonal(self, XPositive, YPositive):
#FIXME 192hgk2u59 key controls are not normalized for diagonal movement.
diagSpeed = (self.speed*self.__diagMove)
if XPositive:
self.x = self.x + diagSpeed
else:
self.x = self.x - diagSpeed
if YPositive:
self.y = self.y + diagSpeed
else:
self.y = self.y - diagSpeed
def moveDiagonalSprink(self, XPositive, YPositive):
#FIXME 192hgk2u59 key controls are not normalized for diagonal movement.
diagSpeed = (self.speed*self.__diagMove)
if XPositive:
##Has to be out of below for loop...
self.distance = self.distance + diagSpeed
for sprinkler in Rotator:
sprinkler.xHead = sprinkler.xHead - diagSpeed
else:
##Same thing hahaha
self.distance = self.distance - diagSpeed
for sprinkler in Rotator:
sprinkler.xHead = sprinkler.xHead + diagSpeed
if YPositive:
self.y = self.y + diagSpeed
else:
self.y = self.y - diagSpeed
##</KEY CONTROL>
def mouse_control(self, xMouse, yMouse):
##Get the xy change
xChange = xMouse - self.x
yChange = yMouse - self.y
##Get the distance between the guy and cursor
totChange = math.sqrt((xChange**2) + (yChange**2))
##Abort if no change, avoid /0
if totChange == 0:
return
##Get proportional x,y to distance = 1
##totChange will always be positive
xInc = xChange/totChange
yInc = yChange/totChange
##Get greatest wind size for dist-speed
(windWidth, windHeight) = self.window.get_size()
if windWidth > windHeight:
windSize = windWidth
else:
windSize = windHeight
##25% of screen size away from guy is max speed
if totChange >= (windSize/4):
speedPerc = 1
else:
##Get percentile of distance between guy and 25% of screen size
speedPerc = totChange/(windSize/4)
##Multiply incs by speed, and that by speedPerc to get final inc
increment = self.speed*xInc*speedPerc
self.distance = self.distance + increment
##Do the opposite for the sprinklers
for sprink in Rotator:
##sprink.xHead = sprink.xHead - increment
sprink.runner_Xmove((increment*-1))
self.y = self.y + (self.speed*yInc*speedPerc)
def getMove(self, keys):
"""keys is a pygame.key.get_pressed() object
This method also uses self.arrows"""
##define control methods
#FIXME the sprinkler movement is incorrect when both mouse and key controls are used simultaneously.
#FIXME simultaneous key and mouse controls give additive speed.
#FIXME 192hgk2u59 key controls are not normalized for diagonal movement.
if self.arrows:
left = keys[pygame.K_LEFT]
right = keys[pygame.K_RIGHT]
down = keys[pygame.K_DOWN]
up = keys[pygame.K_UP]
restart = keys[pygame.K_RSHIFT]
else:
left = keys[pygame.K_a]
right = keys[pygame.K_d]
down = keys[pygame.K_s]
up = keys[pygame.K_w]
restart = keys[pygame.K_LSHIFT]
if restart:
self.x, self.y, self.distance = self.start()
if left:
if up:
self.moveDiagonalSprink(False, False)
elif down:
self.moveDiagonalSprink(False, True)
else:
self.moveXSprink(False)
elif right:
if up:
self.moveDiagonalSprink(True, False)
elif down:
self.moveDiagonalSprink(True, True)
else:
self.moveXSprink(True)
elif up:
self.moveY(False)
elif down:
self.moveY(True)
def reDraw(self, window=None, x=None, y=None, image=None):
"""In this method, image is a pygame image object"""
if x is None:
x = self.x
if y is None:
y = self.y
if window is None:
window = self.window
if image is None:
image = self.image
window.blit(image, (self.x, self.y))
def check_dist_add(self, sprinklerList):
##print self.distance
##If the dude was recently re
if (self.distance == 0) and (self.reInit):
##We've restarted! Don't do it again
self.reInit = False
##ReInit lastPlaced distance
self.lastPlaced = 0
##Clear iterable registry
Rotator._registry = []
sprinklerList = []
sprinklerList = [Rotator(screen, True, teal, randrange(500, 900), randrange(3, 6), randrange(0, WIDTH), randrange(0, HEIGHT), randrange(0, 6), randrange(25, 70), randrange(5, 10)) for name in range(3)]
##Place if we've traveled the travel dist since the last x-coord we placed
elif (self.lastPlaced + 200 < self.distance):
##Track distance last placed
self.lastPlaced = self.distance
sprinklerList.append(Rotator(screen, True, teal, randrange(500, 900), randrange(3, 6), (WIDTH + randrange(100, 400)), randrange(-50, (HEIGHT+50)), randrange(0, 6), randrange(25, 70), randrange(5, 10)))
return sprinklerList
class Rotator(object):
##FIXME
## Needs Value Error and Type Error exception handling for arguments
##/FIXME
##Class elements
##Link to metaclass, define registry
__metaclass__ = IterRegistry
_registry = []
##Internal variables
#FIXME just use math.pi
__pi=3.14
def __init__(self, window, placed, color, fluid, fAmps, xHead, yHead, theta, rotInc, segNum):
"""Speed is the # of frames it takes to go around a full circle
Theta is the starting angle"""
##Append instance to class registry
self._registry.append(self)
##Basic inits
self.window = window
self.color = color
self.fluid = fluid
self.fAmps = fAmps
self.segNum = segNum
##Placed bool: this determines whether to draw the sprinkler
self.placed = placed
##Move arguments to class members accessible by other class definitions
self.xHead = xHead
self.yHead = yHead
self.theta = theta
self.speed = self.__pi/rotInc
self.pressure = self.get_pressure()
self.rnge = self.pressure
self.init_segments()
def init_segments(self):
##Must call this every time the sprinkler is replaced, otherwise there are "invisible segments"
self.xEnd = self.getxEnd()
self.yEnd = self.getyEnd()
self.segments = [Segment(self.window, self.color, self.pressure, self.fAmps, self.segNum, self.xHead, self.yHead, self.xEnd, self.yEnd)]
def get_pressure(self, fAmps = None, fluid = None):
##FIXME This might be divided by a constant? Or should the normal fAmps/fluid ratio just be such that in acts appropriately
##FIXME a quick research of water pressure equations should reveal this.
##Args
if fAmps is None:
fAmps = self.fAmps
if fluid is None:
fluid = self.fluid
##/Args
return (fluid/fAmps)
def getxEnd(self, h=None, r=None, theta=None):
"""Either uses instance variables, or takes arguments.
Returns the x coord end point"""
##Process optional parameters
if h is None:
h = self.xHead
if r is None:
r = self.rnge
if theta is None:
theta=self.theta
##/Process optoinal parameters
return(h + (r * math.cos(theta)))
def getyEnd(self, k=None, r=None, theta=None):
"""Either uses instance variables, or takes arguments.
Retuns the y coord end point"""
##Process optional parameters
if k is None:
k=self.yHead
if r is None:
r=self.rnge
if theta is None:
theta=self.theta
##/Process optoinal parameters
return(k + (r * math.sin(theta)))
def IncrementTheta(self, theta=None, speed=None):
"""Either uses instance variables, or takes arguments."""
##Process optional parameters
if theta is None:
theta=self.theta
if speed is None:
speed=self.speed
##/Process optoinal parameters
##Some sprinklers do funky stuff when being reset to 0
##This needs to be relative to the sprinkler's initial theta...do something with that.
if math.fabs(theta) < math.fabs(((2*self.__pi)-speed)):
theta = theta + speed
else:
theta = 0
##Return theta
return theta
def reDraw(self, window=None, color=None):
"""Increments angle, updates endpoints.
Uses class variables for points draw.
And optional set arguments for window, color, and fluid"""
if not self.segments[-1].forming:
##Process optional parameters
if window is None:
window=self.window
if color is None:
color=self.color
##/Process optoinal parameters
##Increment angle first!
self.theta = self.IncrementTheta()
##Get endpoints second
self.yEnd = self.getyEnd()
self.xEnd = self.getxEnd()
##For dynamic pressure changes, only apply to new segments
self.pressure = self.get_pressure()
self.segments.append(Segment(window, color, self.pressure, self.fAmps, self.segNum, self.xHead, self.yHead, self.xEnd, self.yEnd))
##pygame.draw.line(window, color, (self.xHead, self.yHead), (self.xEnd, self.yEnd), self.fluid)
##Clean finished segments
if self.segments[0].finished:
del self.segments[0]
for seg in self.segments:
seg.reDraw()
def collision(self, runner):
##FIXME this will probably be a param passed to the method eventually.
##FIXME but leave here now for understandings sake.
##FIXME should be replaced in favor of a collision box for the runner
proxLeeway = 3.14/128
##INITS
##Get distance between runner and each segment
runRelX = runner.x-self.xHead
runRelY = runner.y-self.yHead
##/INITS
##RANGE
##Check if runner is in range
##Distance formula
runnerDistance = math.sqrt(((runner.x - self.xHead)**2)+((runner.y - self.yHead)**2))
furthestSegment = self.segments[0]
if runnerDistance > (furthestSegment.traveledLen - (furthestSegment.allowedLen - furthestSegment.actualLen)):
return False
##/RANGE
if len(self.segments) > 1:
##SPAN
##Checking span before looping segments is actually better. Looping through
## every segment of every sprinkler in range takes a lot of power
##Get angle between the first segment and last segment, all water falls between.
##acos of normalized dot product.
##seg.P* is the normalized vector of the full length the segment will travel
##Newest segment to Oldest segment
NOdpNorm = ((self.segments[-1].Px) * (self.segments[0].Px)) + ((self.segments[-1].Py) * (self.segments[0].Py))
spanNO = math.acos(NOdpNorm)
##Newest segment to Runner
NRdpNorm = ((self.segments[-1].Px) * (runRelX/runnerDistance)) + ((self.segments[-1].Py) * (runRelY/runnerDistance))
spanNR = math.acos(NRdpNorm)
##Oldest segment to Runner
ORdpNorm = ((self.segments[0].Px) * (runRelX/runnerDistance)) + ((self.segments[0].Py) * (runRelY/runnerDistance))
spanOR = math.acos(ORdpNorm)
##Check if runner does not double overlap, if he does, he's in the larger angle between the old/new segments
##To detect positively, would check if spanNR <= spanNO >= spanOR, if so, he's between them.
##proxLeeway added in case on outside edge of spanNO
##FIXME if spanNO == pi, he will collide on both sides, but the sprinklers should never have a span of pi?
if (spanNR > (spanNO + proxLeeway)) or (spanOR > (spanNO + proxLeeway)):
return False
##FIXME tshoot span visualizer
pygame.draw.line(self.window, (255, 255, 255), (self.xHead, self.yHead), (((self.segments[0].Px * self.segments[0].rnge) + self.xHead), ((self.segments[0].Py * self.segments[0].rnge) + self.yHead)), 2)
pygame.draw.line(self.window, (255, 255, 255), (self.xHead, self.yHead), (((self.segments[-1].Px * self.segments[-1].rnge) + self.xHead), ((self.segments[-1].Py * self.segments[-1].rnge) + self.yHead)), 2)
##/SPAN
##SUBRANGE
for i, seg in enumerate(self.segments):
##We already know he's in range of the furthest segment, so we can check seg.traveledLen from furthest seg from center
## He's in range and between the newest and oldest segments.
## even if all the segments have not been created, for some segment, runnerDistance < traveledLen.
## that is, this will never break with i<1
if runnerDistance < seg.traveledLen:
pass
else:
break
##See note above, this will not wrongly read the newest segment, i will be > 0
checkSeg = self.segments[i-1]
##/SUBRANGE
## else there is only 1 segment
else:
seg = self.segments[0]
if runnerDistance > seg.traveledLen:
##if runner is outside of the first segment's range
return False
else:
checkSeg = seg
##FIXME tshoot range visualizer
checkRadius = int((checkSeg.traveledLen - (checkSeg.allowedLen - checkSeg.actualLen)))
if checkRadius > 1:
pygame.draw.circle(self.window, (0, 0, 0), (int(self.xHead), int(self.yHead)), checkRadius, 1)
##Get runner span from checkSeg to runner
CRdpNorm = ((checkSeg.xLen/self.rnge) * (runRelX/runnerDistance)) + ((checkSeg.yLen/self.rnge) * (runRelY/runnerDistance))
spanCR = math.acos(CRdpNorm)
if spanCR < proxLeeway:
pygame.draw.line(self.window, (0, 0, 0), (self.xHead, self.yHead), (runner.x, runner.y), 10)
return True
else:
return False
#FIXME use the warning module
print "Weird exception in collision detection! Shouldn't have gotten this far."
def runner_Xmove(self, distance):
self.xHead = self.xHead + distance
for seg in self.segments:
seg.runner_Xmove(distance)
if __name__ == "__main__":
##Main Inits
pygame.init()
WIDTH = 480*2
HEIGHT = 320*2
screen = pygame.display.set_mode((WIDTH, HEIGHT),0, 32)
clock = pygame.time.Clock()
FPS = 40
forestGreen = (34, 139, 34)
darkRed = (139, 0, 0)
teal = (0, 128, 128)
red, green, blue = 90, 200, 60
i = 0
myfont = pygame.font.SysFont("monospace", 30)
##/Main Inits
##Object inits!
## = ( window, True:Arrows/False:WASD, True:Top of screen/False:Bottom of Screen, speed, image path)
##FIXME default speed not remembered, reset to 2 in start
runnerTop = Runner(screen, False, True, 5, 'Images/ShyGuyMSS.PNG')
##Sprinklers are created and added by the runner, per his distance see PROCESSES
##But need init.
sprinks = []
##/Object inits!
##Drawing Loooooop!
while True:
##PROCESSES
##Ask Runner if his distance warrants new sprinklers
##FIXME Using multiple runners...will make a gazillion sprinklers.
for dude in Runner:
##Pass sprinks for append/delete
sprinks = dude.check_dist_add(sprinks)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
for sprinkler in Rotator:
##Sprinklers now start placed
##Check if not placed, place new sprinklers
##if not sprinkler.placed:
## sprinkler.xHead = randrange(50, (WIDTH+300)) ##Spread out sprinklers
## sprinkler.yHead = randrange(-50, (HEIGHT+50))
## sprinkler.placed = True
if not sprinkler.placed:
print 'Woops!'
##Check if sprinkler is off the screen
##FIXME ought to be a method of the sprinkler itself.
if sprinkler.xHead < (-150):
##Move sprinkler
##FIXME ought to be a method of the sprinkler itself.
sprinkler.xHead = sprinkler.xHead + (WIDTH+(randrange(150, 500)))
sprinkler.yHead = randrange(-50, (HEIGHT+50))
sprinkler.init_segments()
randomDir = randrange(1, 3)
if randomDir % 2 == 0:
sprinkler.speed = sprinkler.speed * (-1)
##/PROCESSES
##DRAW and UPDATE OBJECTS
screen.fill((red, green, blue))
#FIXME for performance, combine these two loops with the ones above? for readability, maybe not?
for sprinkler in Rotator:
##Check if sprinkler has been placed before drawing
if sprinkler.placed:
sprinkler.reDraw()
#NOTE TODO 20160520: this run time is O(num sprinkler * num runner), because we just have 1 or 2 runners, this is not a big deal
# BUT, if there were lots of runners, we'd probably want to use a sweep line.
# 1. keep an ordered list of events, by x value, of sprinkler (position - radius) and (position + radius)
# 2. and a stack of ordered runners
# we'd probably use insertion sort to maintain both of these, as only small changes are needed to each on each frame
# and we want constant retrieval time.
# 3. if the event is a left side,
# store the sprinkler in an a list ordered by left position
# store the sprinkler by name in a dictionary/hash table, this will have an index to its list position
# 3. if the event is a right side,
# examine runners in the list until one is encountered past the sweep line
# if the runner is left of the furthest retained left event, discard the runner
# check collisions for each runner to this sprinkler.
# then discard corresponding left event
#
# for each event then, only the runners in the window between the leftest left event and the sweep line will be checked against
# each sprinkler. the best case scenario would be O 2(sprinklers), and would occur if no explorer was examined twice..
# the worst case would be if there was a GIANT sprinkler whose left and right events encompassed all events and runners.
# in this case, all the runners would have to be examined for each event, so we'd have O 2(sprinklers)(runners)
# if we took advantage of the triangle in which all the collision exists, we could do two sweep lines
# the first would have an x-ordered list of left and right events of collision triangles that were more narrow on x than y.
# the second would have a y-ordered list of top and bottom events of collision triangles that were more narrow on y than x.
# these lists would require similarly small changes, so insertion sort could be used.
# sprinklers would switch lists very infrequently, so they could be placed in the other
# list in O logn average case using aquicksort.
#
##Check if a runner has collided!
for oneRunner in Runner:
if sprinkler.collision(oneRunner):
if oneRunner.speed > 0:
oneRunner.speed = oneRunner.speed - (oneRunner.defSpeed*0.10)
if red < 225:
red = red + 30
else: red = 0
for oneRunner in Runner:
##Keyboard
keys = pygame.key.get_pressed()
oneRunner.getMove(keys)
##Mouse Control
mPos = pygame.mouse.get_pos()
oneRunner.mouse_control(*mPos)
oneRunner.reDraw()
##Display score for runnertop
score = myfont.render(str(int(runnerTop.distance)), 1, (255,255,0))
screen.blit(score, ((WIDTH-100), (HEIGHT-100)))
pygame.display.flip()
##/DRAW and UPDATE OBJECTS
clock.tick(FPS)
##/Drawing Loooop!