#はじめに
Qiitaの新サービスQiitadonが500文字までの文章を入力できてコードブロック&マークダウンに対応してると聞いたもので、喜び勇んで現在勉強中のKotlinを使って何か投稿しようとしたのが8年前、ちがった8日前でした。
仕事と家族団らんで時間がとれないなか、ようやくプログラミングしたコードですが、やはり500文字制限は厳しいですね。コメント外しても800文字強。Qiitadonには投稿できません。
もう挫折したので、コードをさらして次の糧にしたいと思います。文字数をおさえるため可読性が落ちてますが、ご容赦くださいませ。
Kotlinいいね!って処をコードのあとのほうでまとめますね。
#コード
import java.awt.Point
fun readl(): Int {//123のどれかの文字を入力受け付けるよ
var ax : String? = readLine();var a=-1
if(ax!=null && ax.length==1) a = "123".indexOf(ax[0], 0, false)
return a+1
}
fun Point.read(){//Pointクラスに拡張関数を追加するよ
while(x<1){print("x?"); x=readl()}
while(y<1){print("y?"); y=readl()}
}
fun main(args:Array<String>) {
var p =0//プレイヤー 0 or 1
var ban =0//3x3の盤
val ptn= arrayOf(86016,1344,21,66576,16644,4161,65793,4368)//勝利判定パターン
while(ban.toString(2).count{it.equals('1')}<9) {//盤が全部うまっていたらゲーム終了するよ
println("["+p+" turn]")//どっちの番か表示しようね
/* 盤の表示 */
for(i in 8 downTo 0){//上位ビットから表示
print(".ox"[(ban shr i*2 ) and 0b11])//盤を2bitずつシフトで移動して、該当箇所を表示 0b00="."/0b01="o"/0b11="x"
if(i % 3 ==0) println()//3文字表示したら改行
}
val pnt=Point();pnt.read()//マークする座標を入力するよ
val point=(0b1 shl (6*(3-pnt.y)) shl ((3-pnt.x)*2)) shl p//座標を盤に変換するよ
if((ban and point== 0) && ((ban shr (1-p) )and (point shr p)==0)){//マークしようとした座標は空いてるよね
ban = ban or point//空いていたら、盤上にマークするよ
if(ptn.find{it and (ban shr p) == it}!=null) {println("" + p + " won!");break}//勝利判定するよ
p= p xor 1//次の人どうぞ
}
}
println("game end")//ループを抜けたってことはゲーム終了だ。お疲れ様。
}
#解説
もうお分かりでしょう、このコードはかつてあわや世界大戦勃発かという事態を阻止した伝説のゲームTIC TAC TOEいわゆる三目並べを実装しています。
1マスを2bitsとして、Int型の碁盤上に二人のプレイヤーが交互にマーキングして行き、縦、横、斜めの3文字判定パターンに一致したら勝利という実装をしています。
###実行例
[0 turn]
...
...
...
x?2
y?2
[1 turn]
...
.o.
...
x?
(略 3目並ぶかマスが埋まるまで続く)
#Kotlinいいね!
折角なのでKotlinっていいね!となった処をまとめました。
###拡張関数
Kotlinでは既存クラスに関数を追加することができます。今回はjava.awt.Pointに対してxとyに標準入力から入力した値を入れる拡張関数read()を追加しました。
import java.awt.Point
//略
fun Point.read(){//Pointクラスに拡張関数を追加するよ
while(x<1){print("x?"); x=readl()}
while(y<1){print("y?"); y=readl()}
}
fun main(args:Array<String>) {
//(略)
val pnt=Point();pnt.read()//マークする座標を入力するよ
//(略)
}
###NULL安全
Kotlinでは、nullが許容される変数とされない変数を区別しています。変数定義の際にnull許容型なのかわかりますので、その変数に対してnull対策を忘れることはないでしょう。というか対策しないとコンパイルが通りません。
今回、標準入力を取得するため使用したreadLine()はString?型を返却する関数なので、変数もString?で定義しています。型名称の後の"?"でnull許容型かどうかの印ですね。String?はnull許容型なので、その後はnullチェックしないとコンパイルでエラーとなります。
var ax : String? = readLine();var a=-1
if(ax!=null && ax.length==1) a = "123".indexOf(ax[0], 0, false)
###演算子
Kotlinでもほかの言語同様に論理演算が可能です。
今回は盤のマーキングや判定にshr,shl,and,or,xorを使用しました。今回使用した演算子の記述例を以下にまとめました。
演算子 | 説明 | 記述例 | 演算後 |
---|---|---|---|
shr | 右シフト | 0b10 shr 1 | 0b01 |
shl | 左シフト | 0b01 shl 1 | 0b10 |
or | 論理和 | 0b1001 or 0b1010 | 0b1011 |
and | 論理積 | 0b1001 and 0b1010 | 0b1000 |
xor | 排他的論理和 | 0b1001 xor 0b1010 | 0b0011 |
infix記法を使うことで、0b10.shr(1)ではなく、0b10 shr 1と記述することができるのが特徴ですね。
###ラムダ式
Kotlinはラムダ式が使えて、超便利です。今回は、文字列のcount関数にカウントする条件{it.equals('1')}
をラムダ式で渡しています。
while(ban.toString(2).count{it.equals('1')}<9) {//盤が全部うまっていたらゲーム終了するよ
抜粋したコードは9マスの盤面全部が埋まったらゲームのループから抜ける判定を行っている箇所です。
###文字操作
今回のプログラムでは以下のような文字列操作を行っています。
print(".ox"[(ban shr i*2 ) and 0b11])//盤を2bitずつシフトで移動して、該当箇所を表示 0b00="."/0b01="o"/0b10="x"
String型の要素は[]で配列のようにアクセスすることができます。
抜粋したコードは、盤上の表記をまとめた文字列".ox"から、ban変数に2bitsで記録されたマーク情報(0b00:なし、0b01:プレイヤー0、0b10:プレイヤー1)を索引として文字を取り出して表示しています。
#おわりに
この記事では全然表現しきれていませんが、Kotlinはプログラミングしていると、ああこれ楽だ!を良く味わえる良い言語です。ぜひ皆さんも使ってみてください。
ではまた!