はじめに
初投稿です。タイトルの通り、HTMLでスネークゲームを作りました。メモ帳にコピペして拡張子を変えればプレイできます。矢印キーで操作しましょう。
ソースは284文字(293バイト)。jsだと一番短く書けてるんじゃないかな?
で、そのソースがコレ
<body onload='C=c=>!(c%16)|c<17|c>256|S.includes(c);G=_=>{if(C(a))for(l++;C(a=Math.random()*s|0););C(h+=d%2?d*16:d+1)||setTimeout(G,s);S[m-l]=0;S[++m]=h;for(D=l,i=0;i<s;B.innerHTML=D+=++i%16?C(i)?"■":i-a?" ":"●":"■<br>■")onkeydown=e=>d=e.which-39};G(d=l=m=-1,s=271,S=[h=a=136])'id=B>
多分意味が分からないと思うので、解説を見て下さい。
かなり7行テトリスに似ていることに途中で気づきました。コピペとかはしてません
解説
変数・関数
h...head ヘビの頭の座標
l...length ヘビの長さ
d...direction ヘビの向き
s...boardsize 盤面の大きさ。ループ間隔にも流用しています
a...apple エサの座標
m...移動回数カウント用
i...雑用カウント用
D...display 描画用
S...snake ヘビの身体の座標を格納する配列
C...cell 引数cの座標が"■"(壁・ヘビの身体)ならtrueを返す関数
G...game メインループ
名前に文字数なんて使ってられないです。全て1文字。可読性は捨てましょう
関数Gについて
1.エサの判定
2.ゲームオーバーの判定
3.ヘビの移動
4.描画
の順番で処理します。
1.エサの判定
if(C(a))for(l++;C(a=Math.random*s|0);)
関数Cでエサの座標にヘビの座標が重なっているかを判定します。重なっていたら、lを加算しつつfor文で関数Cで空白にエサが置けるまでループします。
2.ゲームオーバーの判定
C(h+=d%2?d*16:d%1)||setTimeout(G,s)
頭の移動はここで行っていますが、Sはまだいじりません。左に移動したいなら座標を-1、上なら-16、右なら+1、下なら+16すればいいというようになっているので、それをここで処理し、その座標が"■"なら||演算子でループを止めます。
3.ヘビの移動
S[m-l]=0;S[++m]=h
初めはshift()とpush(h)を使っていたのですが、色々な事情でこちらの方が適していると判明しました。やっていることは変わらないです。
描画
for(D=l,i=0;i<s;B.innerHTML=D+=++i%16?C(i)?"■":i-a?" ":"●":"■<br>■")
iを回し、関数Cでiの座標"■"かを判定し、i-aでiが"●"かを判定します。ちなみに、lが3桁になると画面上部が少し見にくくなります。(訳あって画像は用意できません。m(__)m)
イベントについて
このソースではonloadイベントにjsの処理を全て詰め込んでいます。これによりscriptタグを省略できます。
また、onkeydownイベントが変な場所で設定されていますが、これは適当に色試行錯誤してたらうまくいったってだけなので、なぜ動作しているのかは謎です。
おわり
最後まで読んでくれてありがとうございます。
今は2048のショートコーディングに挑んでいるので、また機会があればお会いしましょう。