大きく3種類の道から成っているステージです。両側の砂の道、草の道、そして真ん中のフロート板7枚で構成された道です。真ん中のフロート板の道を1段階上げてやると、両側の道の片方の端と高さが揃って、歩いて行き来できる一本道になります。ので、N字のように往復しながら宝石を規定数(totalGem)集めていきます。
まずフロート板の上げ下げ係のexくん(Expert型)を作り、スイッチ前に移動させます。彼の仕事はフロート板を1段終わりだけで終わりです。
次にchくん(Character型)を作りスタート地点に移動。今回は3つの道のそれぞれで、端から端まで移動しつつ宝石を取る動作をひとまとまりとし、関数jumpToEdge()を作りました。砂の道と草の道は段差があるのでmoveForward()だけでなくjump()も使い分けなければなりません(ぶっちゃけ全部jump()で進めるのですが、まぁプログラミングの練習なので一応if文で行き止まりかどうかで分岐させてます)。とった宝石の数はgemsという変数で記録していきます。collectGem()したら必ず+=1しておきましょう。
本編では「規定数の宝石を集める」のがゴールなので、「とった宝石数gemsが規定数totalGemに満たない場合は繰り返す」whileループをメインループとしています。その中でjumpToEdge()を呼んで今いる道の反対側の端まで行き、端の行き止まり状況に応じて左右に向きをかえる分岐をしています。
2019.1.17編集
Swift 4.2版では昇降スイッチの位置が変わったようで、それを操作するexくんの配置座標をatColumn:1 row:4からAtColumn:0 row:4に変更しました。
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 |
let totalGem = randomNumberOfGems //totalGemに取るべき宝石の数がセットされる(毎回違う) //昇降スイッチを操作するexくんを作成して配置 let ex = Expert() //world.place(ex, facing: north, atColumn: 1, row: 4) //旧バージョンではatColumn:1でした world.place(ex, facing: north, atColumn: 0, row: 4) //宝石を取るchくんを作成して配置 let ch = Character() world.place(ch, facing: north, atColumn: 3, row: 0) var gems = 0 //取った宝石の数を記録する変数 //今いる道を突き当たりまで進みつつ、宝石があれば取る関数 func jumpToEdge() { for i in 1 ... 6 { //宝石があり、かつまだ取り足りなければ if ch.isOnGem && gems < totalGems{ ch.collectGem() gems += 1 //取ったらカウンタも1増やしていく } //行き止まりならジャンプ、でなければ前進 if ch.isBlocked { ch.jump() } else { ch.moveForward() } } } ex.turnLock(up: true, numberOfTimes: 1) //exくんにフロート板を操作させる //ここから本編 //規定数の宝石を集めてないうちはループ while gems != totalGems { jumpToEdge() //作成した関数を呼び出す //上の関数で端まて来たら隣の道に移動する。 //左右とも行き止まりなら振り返る if ch.isBlockedLeft && ch.isBlockedRight { ch.turnRight() ch.turnRight() //右が行き止まりなら左の道へ } else if ch.isBlockedRight { ch.turnLeft() ch.moveForward() ch.turnLeft() //左が行き止まりなら右の道へ } else if ch.isBlockedLeft { ch.turnRight() ch.moveForward() ch.turnRight() } } |
橋自体もジャンプで超えれますので、真ん中の橋の操作は一度だけで済みます。
最初に橋の操作を一度だけ2回分上げて、あとはL字型の移動経路を目的達成までループさせてやれば20行程のコードでクリアできました。
お疲れ様です。
Jump()使えませんでした。学習効率上げてるのでしょうか。
稚拙ながら作ってみました。
let totalnumberGems =randomNumberOfGems
let ex1 = Expert()
let ex2 = Expert()
var countGem = 0
world.place(ex1, facing: .north, atColumn: 0, row: 4)
world.place(ex2, facing: .west, atColumn: 4, row: 0)
func forward() {
if ex2.isOnGem {
ex2.collectGem()
countGem += 1
ex2.moveForward()
} else {
ex2.moveForward()
}
}
func left() {
if ex2.isOnGem {
ex2.collectGem()
countGem += 1
ex2.turnLeft()
} else {
ex2.turnLeft()
}
}
func Right() {
if ex2.isOnGem {
ex2.collectGem()
countGem += 1
ex2.turnRight()
} else {
ex2.turnRight()
}
}
func turnAround() {
if ex2.isOnGem {
ex2.collectGem()
countGem += 1
ex2.turnLeft()
ex2.turnLeft()
} else {
ex2.turnLeft()
ex2.turnLeft()
}
}
func abyssGoUp() {
while ex2.isBlocked {
if ex2.isBlocked {
ex1.turnLockUp()
}
}
}
func abyssGoDown() {
while ex2.isBlocked {
if ex2.isBlocked {
ex1.turnLockDown()
}
}
}
func abyssGoUp2() {
abyssGoUp()
forward()
abyssGoUp()
forward()
turnAround()
forward()
}
while countGem < totalGems {
abyssGoUp2()
left()
forward()
Right()
abyssGoDown()
forward()
turnAround()
abyssGoUp2()
left()
forward()
Right()
abyssGoDown()
forward()
turnAround()
abyssGoUp2()
left()
forward()
Right()
abyssGoDown()
forward()
turnAround()
forward()
Right()
ex2.move(distance: 3)
left()
abyssGoDown()
forward()
turnAround()
}
最新のSwift4.2版で試してみましたがjump()使えましたよ。22行目からの分岐を変更してすべてjump()を使う様にしてもクリアできました。
ただし最初のexくんを配置する位置を変更する必要がありました。それ以外は3.1版のコードで動いているようです。
貼っていただいたコードでもクリアできますね。ただしもう少し短くできるかも知れません。例えば、abyssGoUp()やabyssGoDown()で、
while ex2.isBlocked {
if ex2.isBlocked {
…
}
}
となっていますが、while ex2.isBlockedの間は、if ex2.isBlocked常に真になるので、ifはなくでも同じ結果になります。
最後のwhileループの中も同じ様なパターンを繰り返しているようなので、もう少しまとめられるかも知れません。
Programmingは、”The shorter, the better.” “Simple is best.”と聞きました。 重複しているところは、function にして、reuse すればどうでしょうか?
コメントでジャンプできないという内容がありましたが、エキスパートにジャンプの機能はないのでバージョンの問題ではないと思いますよ。
コード内容について関数にifを使う時にelseが不要なところが多いなと思いました。
例えばforward()ではifの中にあるex2.moveForward()を外に出せばもう少しコードが短くなります。
Character型を引数で使えると分かったので、キャラクターのみでクリアしてみました。まさにカエルぴょこぴょこ…。
複数のキャラにターンとかジェムを取るという操作を指示するのが楽になりそうです。
下記コードではエキスパートにターンとかの操作をさせる機会がありませんでしたが、エキスパートも親型がキャラクターなのでターンできます。
turn(character: expert) みたいにすればターンするエキスパートに。
var counterJem = 0
let character1 = Character()
let character2 = Character()
let character3 = Character()
func colectingJem(character: Character) {
if character.isOnGem {
character.collectGem()
counterJem += 1
}
}
func turn(character: Character) {
for i in 1 … 2 {
character.turnLeft()
}
}
world.place(character1, facing: north, atColumn: 2, row: 0)
world.place(character2, facing: north, atColumn: 4, row: 0)
world.place(character3, facing: north, atColumn: 3, row: 0)
while totalGems != counterJem {
for i in 1 … 6 {
colectingJem(character: character1)
colectingJem(character: character2)
colectingJem(character: character3)
character1.jump()
character2.jump()
character3.moveForward()
}
turn(character: character1)
turn(character: character2)
turn(character: character3)
}
いつも適切なCordingに感謝しています。小さなことですが、最後の「右が行き止まりなら左の道へ」と「左が行き止まりなら右の道へ」の中の「ch.moveForward()」ですが、もし、ここにジェムがあった場合、ミスして通り過ぎてしまいます。(時々、ジェムがコーナーに出ることがある場合)もちろん、ループさせているので、あとで、取れますが、ちょっと気になりました。
左右の山と中央の道の3構成と捉え、
1.山用の関数
2.中央の道用の関数
をループさせることで、2つの関数を呼び出すだけで完結できたので貼っておきます。
↓↓↓
let expert = Expert()
let character = Character()
var Gems = 0
//指定の回数ジャンプし、道中の宝石を取る
func jumping(distance:Int){
for i in 1 … distance {
character.jump()
if character.isOnGem{
character.collectGem()
Gems += 1
}
}
}
//ジャンプを6回行い、道中の宝石を取って折り返す
func walking() {
jumping(distance: 6)
character.turnLeft()
character.turnLeft()
jumping(distance: 6)
character.turnRight()
character.moveForward()
character.turnRight()
if character.isOnGem{
character.collectGem()
Gems += 1
}
}
//中央の道を移動し道中の宝石を取る
func goStraight() {
character.move(distance: 6)
character.turnLeft()
character.moveForward()
character.turnLeft()
if character.isOnGem{
character.collectGem()
Gems += 1
}
}
//ここから本編
world.place(expert, facing: .north, atColumn: 0, row: 4)
world.place(character, facing: .north, atColumn: 4, row: 0)
expert.turnLock(up: true, numberOfTimes: 1)
while Gems < totalGems {
walking()
goStraight()
}