@@ -48,10 +48,12 @@ class GameScene: SKScene, SKPhysicsContactDelegate {
48
48
var generationLabel : SKLabelNode !
49
49
var fitnessLabel : SKLabelNode !
50
50
51
+ let groundTexture = SKTexture ( imageNamed: " land " )
51
52
52
53
/// Best score (regardless of generation)
53
54
var bestScore : Int = 0
54
55
56
+
55
57
/// Label that displays the best score (bestScore attribute)
56
58
var bestScoreLabel : SKLabelNode !
57
59
@@ -93,7 +95,7 @@ class GameScene: SKScene, SKPhysicsContactDelegate {
93
95
moving. addChild ( pipes)
94
96
95
97
// ground
96
- let groundTexture = SKTexture ( imageNamed : " land " )
98
+
97
99
groundTexture. filteringMode = . nearest // shorter form for SKTextureFilteringMode.Nearest
98
100
99
101
let moveGroundSprite = SKAction . moveBy ( x: - groundTexture. size ( ) . width * 2.0 , y: 0 , duration: TimeInterval ( 0.02 * groundTexture. size ( ) . width * 2.0 ) )
@@ -207,7 +209,9 @@ class GameScene: SKScene, SKPhysicsContactDelegate {
207
209
self . addChild ( fitnessLabel)
208
210
209
211
// Set the current bird
210
- currentBird = flappyBirdGenerationContainer ? [ currentFlappy]
212
+ if let generation = flappyBirdGenerationContainer {
213
+ currentBird = generation [ currentFlappy]
214
+ }
211
215
212
216
}
213
217
@@ -293,22 +297,17 @@ class GameScene: SKScene, SKPhysicsContactDelegate {
293
297
currentTimeForFlappyBird = NSDate ( )
294
298
295
299
// Evaluate the current birds fitness
296
- currentBird? . generateFitness ( score: score, time: Float ( timeInterval) )
297
-
298
- // Current generation, bird, and fitness of current bird information
299
- print ( " --------------------------- \n " )
300
- print ( " GENERATION: \( generationCounter) " )
301
- print ( " BIRD COUNT: \( currentFlappy) " )
302
- print ( " FITNESS: \( currentBird? . fitness) " )
303
- self . generationLabel. text = " Gen: \( self . generationCounter) "
304
- print ( " --------------------------- \n " )
305
-
306
- // Go to next flappy bird
307
- currentFlappy += 1
300
+ if let bird = currentBird {
301
+ bird. generateFitness ( score: score, time: Float ( timeInterval) )
308
302
309
- // Filter out the worst birds after gen 6 (can be adjusted)
303
+ // Current generation, bird, and fitness of current bird information
304
+ print ( " --------------------------- \n " )
305
+ print ( " GENERATION: \( generationCounter) " )
306
+ print ( " BIRD COUNT: \( currentFlappy) " )
307
+ print ( " FITNESS: \( bird. fitness) " )
308
+ self . generationLabel. text = " Gen: \( self . generationCounter) "
309
+ print ( " --------------------------- \n " )
310
310
311
- if let bird = currentBird {
312
311
if bird. fitness >= 9.0 {
313
312
print ( " FOUND RARE BIRD " )
314
313
print ( bird. brain? . layers [ 0 ] . weights)
@@ -318,10 +317,17 @@ class GameScene: SKScene, SKPhysicsContactDelegate {
318
317
}
319
318
}
320
319
321
- if generationCounter >= 3 {
320
+ // Go to next flappy bird
321
+ currentFlappy += 1
322
322
323
+ if let generation = flappyBirdGenerationContainer {
323
324
// Experiment: Keep some of the last best birds and put them back into the population
324
- lastBestGen = ( flappyBirdGenerationContainer? . filter ( { $0. fitness >= 7.0 } ) ) !
325
+ lastBestGen = generation. filter ( { $0. fitness >= 6.0 } )
326
+
327
+ if lastBestGen. count > 10 {
328
+ // We want to make room for unique birds
329
+ lastBestGen = Array < FlappyGenome > ( lastBestGen [ 0 ... 6 ] )
330
+ }
325
331
}
326
332
327
333
if let bird = currentBird {
@@ -341,8 +347,11 @@ class GameScene: SKScene, SKPhysicsContactDelegate {
341
347
// New generation array
342
348
var newGen : [ FlappyGenome ] = [ ]
343
349
344
- newGen += lastBestGen
345
- newGen. append ( maxBird!)
350
+ newGen = lastBestGen
351
+
352
+ if let bestBird = maxBird {
353
+ flappyBirdGenerationContainer? . append ( bestBird)
354
+ }
346
355
347
356
while newGen. count < 20 {
348
357
@@ -382,17 +391,26 @@ class GameScene: SKScene, SKPhysicsContactDelegate {
382
391
flappyBirdGenerationContainer = newGen
383
392
384
393
// Set the current bird
385
- if ( flappyBirdGenerationContainer? . count) ! > currentFlappy {
386
- currentBird = flappyBirdGenerationContainer ? [ currentFlappy]
387
- } else {
388
- currentBird = maxBird
394
+
395
+ if let generation = flappyBirdGenerationContainer {
396
+ if generation. count > currentFlappy{
397
+ currentBird = generation [ currentFlappy]
398
+ } else {
399
+ if let bestBird = maxBird {
400
+ currentBird = maxBird
401
+ }
402
+ }
389
403
}
390
404
391
405
} else {
392
406
393
407
// Set the current bird
394
- if ( flappyBirdGenerationContainer? . count) ! > currentFlappy {
395
- currentBird = flappyBirdGenerationContainer ? [ currentFlappy]
408
+ if let generation = flappyBirdGenerationContainer {
409
+ if generation. count > currentFlappy {
410
+ currentBird = generation [ currentFlappy]
411
+ }
412
+ } else {
413
+ currentBird = maxBird
396
414
}
397
415
398
416
}
@@ -402,8 +420,7 @@ class GameScene: SKScene, SKPhysicsContactDelegate {
402
420
override func touchesBegan( _ touches: Set < UITouch > , with event: UIEvent ? ) {
403
421
if moving. speed > 0 {
404
422
for _ in touches { // do we need all touches?
405
- bird. physicsBody? . velocity = CGVector ( dx: 0 , dy: 0 )
406
- bird. physicsBody? . applyImpulse ( CGVector ( dx: 0 , dy: 30 ) )
423
+
407
424
}
408
425
} else if canRestart {
409
426
self . resetScene ( )
@@ -457,6 +474,16 @@ class GameScene: SKScene, SKPhysicsContactDelegate {
457
474
458
475
let normalizedDistanceOfNextPipe = ( distanceOfNextPipe - 3 ) / ( 725 - 3 )
459
476
477
+ let distanceFromTheGround = abs ( self . bird. position. y - self . moving. children [ 1 ] . position. y)
478
+
479
+ let normalizedDistanceFromTheGround = ( distanceFromTheGround - 135 ) / ( 840 - 135 )
480
+
481
+ let distanceFromTheSky = abs ( 880 - self . bird. position. y)
482
+
483
+ let normalizedDistanceFromTheSky = distanceFromTheSky/ 632
484
+
485
+
486
+
460
487
// Bird Y position
461
488
let birdYPos = bird. position. y/ CGFloat( 880 )
462
489
@@ -465,25 +492,31 @@ class GameScene: SKScene, SKPhysicsContactDelegate {
465
492
466
493
let normalizedPosToGap = ( posToGap - ( - 439 ) ) / ( 279 - ( - 439 ) )
467
494
468
- // Decision AI makes
469
- let input = Matrix < Float > ( rows: 4 , columns: 1 , elements: [ Float ( normalizedDistanceOfNextPipe) , Float ( normalizedPosToGap) , Float ( birdYPos) , Float ( normalizedDistanceFromBottomPipe) ] )
470
- let decision = currentBird? . brain? . feedforward ( input: input)
495
+ if let flappyBird = currentBird {
471
496
472
- print ( " FLAPPY BIRD DECISION: \( decision) " )
497
+ // Decision AI makes
498
+ let input = Matrix < Float > ( rows: 6 , columns: 1 , elements: [ Float ( normalizedDistanceOfNextPipe) , Float ( normalizedPosToGap) , Float ( birdYPos) , Float ( normalizedDistanceFromBottomPipe) , Float ( normalizedDistanceFromTheGround) , Float ( normalizedDistanceFromTheSky) ] )
499
+ let potentialDescision = flappyBird. brain? . feedforward ( input: input)
473
500
474
- // 0.95 was arbitrary, tweaking is recommended
475
- if ( decision? . elements [ 0 ] ) ! >= Float ( 0.5 ) {
476
501
477
- if moving . speed > 0 {
502
+ if let decision = potentialDescision {
478
503
479
- bird. physicsBody? . velocity = CGVector ( dx: 0 , dy: 0 )
480
- bird. physicsBody? . applyImpulse ( CGVector ( dx: 0 , dy: 30 ) )
504
+ print ( " FLAPPY BIRD DECISION: \( decision) " )
481
505
482
- }
506
+ if ( decision . elements [ 0 ] ) >= Float ( 0.5 ) {
483
507
484
- } else {
508
+ if moving. speed > 0 {
509
+
510
+ bird. physicsBody? . velocity = CGVector ( dx: 0 , dy: 0 )
511
+ bird. physicsBody? . applyImpulse ( CGVector ( dx: 0 , dy: 30 ) )
512
+
513
+ }
514
+
515
+ }
516
+ }
485
517
486
518
}
519
+
487
520
}
488
521
489
522
if canRestart {
0 commit comments