プログラム冒頭でrandomNumberOfGemsというところで宝石回収ノルマ数が決められ、変数totalGemsにセットされます。そこはそういうものだと割りきって、ともかくtotalGems個だけ宝石を集めるまで繰り返すループを組みます。
例によってナビゲーション処理と宝石処理をわけて考えます。宝石処理はいつも通り、isOnGemなら回収してカウンターに1を追加するだけです。
ナビゲーションですが、今回は右に曲がる場合、左に曲がる場合、そして振り返る場合があります。ワープゾーンを加味して考えるとこのステージは1本道で、規定の宝石を集めるまでずっと行ったり来たりを繰り返すわけです。
まずいずれの場合にせよ前方が行き止まっている場合になるので、一番外側にisBlockedを条件に置きます。これで後のここのif文に「&& isBlocked」を書く手間が省け、コードがスッキリします。さらにコースをじっくり観察すると、前方が行き止まりで、かつ、
- 両側も行き止まりなら振り返る
- 左が行き止まりなら右へ
- 右が行き止まりなら左へ
という考え方ができそうです。これをelse文でつなぐと、前条件に漏れた場合だけ判定することになります。例えば最初の2つをもしelseを使わないで、
1 2 3 4 5 6 7 |
if isBlockedLeft && isBlockedRight { //左右とも行き止まりか? turnRight() turnRight() } if isBlockedLeft && !isBlockedRight{ //左が行き止まりかる右が行き止まりでないか? turnRight() } |
と書くと、isBlockedLeftかつisBlockedRightだった時には、両方ともtrueになり、turnRightが3回実行されてしまいます。elseを使わないで、それぞれが重複してtrueにならないようにするには、と書きます。これでも正しいのですが、条件式が長くなりがちです。なので、下の正解例ではelseを使って「前の条件に漏れていて、かつ」という形にしています。すると最後はelseだけになっているわけですが、この理由を納得いくまで考えてみてください。
1 2 3 4 5 6 7 |
if isBlockedLeft && isBlockedRight { //左右とも行き止まりか? turnRight() turnRight() } if isBlockedLeft{ //左が行き止まりか? turnRight() } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
let totalGems = randomNumberOfGems var gemCounter = 0 while gemCounter < totalGems { moveForward() if isOnGem { collectGem() gemCounter += 1 } if isBlocked { //A.正面が行き止まり if isBlockedLeft && isBlockedRight { //B.左右が行き止まり(かつA)→(正面と)左右が行き止まり turnRight() turnRight() } else if isBlockedLeft{ //C.左が行き止まり(ただしBではない)→(正面と)左が行き止まり turnRight() } else { //D. BでもCでもない。ただしAではある→(正面と)右が行き止まり turnLeft() } } } |