トラック
/
Go
Go
/
演習
/
チェス盤
チェス盤

チェス盤

学習演習

はじめに

Goでは、forとインデックスを使ってsliceを繰り返し処理することもできますし、rangeを使うこともできます。rangeを使えば、mapを繰り返し処理することもできます。

それぞれの繰り返しでは、インデックスかキーと、そのインデックスかキーにある要素のコピーの、2つの値が返されます。

sliceを繰り返し処理する

とても簡単です。sliceの繰り返しは、期待どおりの順序で進みます。

xi := []int{10, 20, 30}
for i, x := range xi {
  fmt.Println(i, x)
}
// outputs:
// 0, 10
// 1, 20
// 2, 30

mapを繰り返し処理する

mapを繰り返し処理すると、新たな問題が生じます。順序がランダムになるのです。

hash := map[int]int{9: 10, 99: 20, 999: 30}
for k, v := range hash {
  fmt.Println(k, v)
}
// outputs, for example:
// 99 20
// 999 30
// 9 10
Note

上の出力は正しくないように見えるかもしれません。mapの宣言にある最初のキーと値のペア9 10が、最後ではなく最初に出力されると期待するのが自然だからです。 しかし、mapは本質的に順序を持ちません。最初や最後のキーと値のペアというものはないのです。 そのため、mapの要素を繰り返し処理するとき、どの要素がどの順序でたどられるかはランダムで、決まったパターンには従いません。 つまり、上の出力は起こりえますが、自分で実行したときに得られる結果とは異なるかもしれません。 詳しくは、Go Language Spec: range clauseを参照してください。

キーや値を省略した繰り返し

Goでは、使われていない変数があるとビルド時にエラーになります。 最初の例のように、値だけが必要なこともあります。

xi := []int{10, 20, 30}
for i, x := range xi {
  fmt.Println(x)
}
// Go build failed: i declared but not used

iを_に置き換えると、その値を使わないことをコンパイラーに伝えられます。

xi := []int{10, 20, 30}
for _, x := range xi {
  fmt.Println(x)
}
// outputs:
// 10
// 20
// 30

インデックスだけを出力したい場合は、xを_に置き換えるか、宣言そのものを省略します。

xi := []int{10, 20, 30}
// for i, _ := range xi {
for i := range xi {
  fmt.Println(i)
}
// outputs:
// 0
// 1
// 2

型を定義する

型定義では、新しい名前付きの型を作ります。その型は、たとえ基底型が同じであっても、他のどの型とも異なる別個の型です。

type File []bool

ここでのFileは、チェスボードの縦1列を指します。この列には、ふつうAからHのラベルが付いています。 この列のマスはそれぞれ、そこにコマが置かれているかどうかを示す真偽値で表されます。 Fileが定義できたので、今度はこれを他の定義の中で使えます。

type Chessboard map[string]File

board := Chessboard{
    "A": File{true, false, true},
    "B": File{false, true, false},
}

Chessboardは、ファイルの識別子をFileの値に対応付けます。 それぞれのFileは、チェスの1つのファイルにあるマスを表す真偽値のsliceです。 こうした型定義がないと、boardはmap[string][]boolになってしまい、何を表しているのかが分かりにくくなります。

説明

チェスの愛好家なら、自分だけのバージョンのゲームを書いてみたいと思うのではないでしょうか。確かに、ネット上にはすでにたくさんのチェスの実装が公開されているかもしれません。でも、自分で作るものは唯一無二です!

チェス盤の各マスは、アルファベットと数字の組み合わせで表されます。

  • 横方向のマスの並びはランクと呼ばれ、1から8までの番号が付けられています。
  • 縦方向のマスの並びはファイルと呼ばれ、AからHまでのラベルが付けられています。
   A B C D E F G H
 8 # _ _ _ # _ _ # 8
 7 _ _ _ _ _ _ _ _ 7
 6 _ _ _ _ # _ _ # 6
 5 _ # _ _ _ _ _ # 5
 4 _ _ _ _ _ _ # # 4
 3 # _ # _ _ _ _ # 3
 2 _ _ _ _ _ _ _ # 2
 1 # _ _ _ _ _ _ # 1
   A B C D E F G H

1. チェス盤とファイルから、埋まっているマスの数を数える

CountInFile(board Chessboard, file string) int関数を実装しましょう。 マップをrangeで繰り返し処理して、埋まっているマスの合計数を数えます。 整数を返します。 指定されたファイルがマップの中に見つからない場合は、0を返します。

CountInFile(board, "A")
// => 3

2. チェス盤とランクから、埋まっているマスの数を数える

CountInRank(board Chessboard, rank int) int関数を実装しましょう。 指定されたランクをrangeで繰り返し処理して、埋まっているマスの合計数を数えます。 整数を返します。 指定されたランクが有効でない場合(1から8までに含まれない場合)は、0を返します。

CountInRank(board, 2)
// => 1

3. 与えられたチェス盤にあるマスの数を数える

CountAll(board Chessboard) int関数を実装しましょう。 チェス盤にあるマスの数を数え、整数を返します。 マスの中身を調べる必要はないので、indexとvalueの両方を省略したrangeを使うことを考えてみましょう。

CountAll(board)
// => 64

4. 与えられたチェス盤で埋まっているマスの数を数える

CountOccupied(board Chessboard) int関数を実装しましょう。 チェス盤で埋まっているマスの数を数えます。 整数を返します。

CountOccupied(board)
// => 15
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Go Exercism

チェス盤を始める準備はできましたか?

Exercismに登録すれば、34個のコンセプト166個の演習、そして本物の人間によるメンタリングとともに、Goを学んでマスターできます。すべて無料です。