数学デー派生ゲーム「亜・矢印回転ゲーム」 on Colaboratory
【ご挨拶】
明けましておめでとうございます。
9ヶ月ぶりですね。
生存報告のために問題作って解説作って揚げようとしたらいつの間にか忘れ去られておりました。ぐべぇ。
今のところ特に病気も罹っていないため、安心してください。
(体温全く測っていないので気づいていないだけだからかもしれないけれど)
【本題】
最近というか、ここ10ヶ月ほど数学デーに全く顔を出しておりませんでしたね。
(2/8をおそらく最後に全くDiscoにも神保町オフラインのほうにも)
さて私(わたくし)、高2から学校で「情報I」を履修したことで、
コミュニティ・バンされたブロック構築型のプログラミング言語・Scratch
からもおさらば!
新しく触る構文型プログラミング言語・Python
という大きな武器を手に入れました!
ということで、ふと、以前オンラインで触れていた数学デーのゲームを再現しようと思い立ちました、と。
こちらですね。
「4人用オセロ」と仮題がつけられてあります。
PIXIV FANBOX で私、数学デーさんの支援をしておりましたが、現在は全くできておりません。それにより、当時のゲーム性がどういうものであったか、それは、自分の過去ツイートや周りの方々の証言を用ゐでは、え把握せぬことなり、なのですよ。(なぜ急に古文調)
(あ、これからも受験が終わるまでは数学デーにほとんど顔を出せないと思われるため、ご了承ください。)
ということで今回、再現と云えばよいのか分かりませんが、多少当時とルールなどがずれているとは思いますがゲームのプログラムを作ってみました。
以下 コードです。
【Code】
import math
def PLAYER():
print("-Player Regestrations-")
N=int(input("How many players:"))
L=[]
SC=[] #Score
for i in range(1,1+N):
L.append(input("Player"+str(i)+":"))
SC.append(0)
return [L,SC]
def SIZE():
print("-Board Size Regestrations-")
W=-1
while W<2:
W=int(input("Wide: "))
H=-1
while H<2:
H=int(input("Height: "))
return [W,H]
def Board_Generation(MAP,h,w):
for i in range(h):
Rows=[]
for j in range(w):
Rows.append(-1)
MAP.append(Rows)
def Display(M):
for i in range(len(M)):
string=""
for j in range(len(M[i])):
if M[i][j]==-1:
string+="+" #未開地は "+"
else:
string+=Arrows[M[i][j]%4] # ['A', 'B', 'C'] の配列を「A B C」と表示
if j==len(M[i])-1: #以下、5要素ごとに空白
string+=""
elif j%5==4:
string+=" "
else:
string+=" "
print(string)
if i!=len(M)-1: #以下、5要素ごとに空白
if i%5==4:
print()
#=========================================
Arrows=["→","↓","←","↑"]
WASD=["D","S","A","W"]
PL=PLAYER()
Player=PL[0]
Score=PL[1]
Size=SIZE()
height =Size[1]
wide =Size[0]
MAP=[]
Board_Generation(MAP,height,wide)
Display(MAP)
#=========================================
def Judge(Y,X):
MM=[]
if X==wide:
MM.append(-1)
else:
MM.append(MAP[Y-1][X])
if Y==height:
MM.append(-1)
else:
MM.append(MAP[Y][X-1])
if X==1:
MM.append(-1)
else:
MM.append(MAP[Y-1][X-2])
if Y==1:
MM.append(-1)
else:
MM.append(MAP[Y-2][X-1])
return MM
def Position(N,t):
Cond=0
while Cond==0:
A=-1
a=0
while a==0:
A=int(input("From Leftside:")) #x=A-1
if 1<=A<=wide:
a=1
else:
print("Over the board.Try it again.")
B=-1
b=0
while b==0:
B=int(input("From Upside:")) #y=B-1
if 1<=B<=height:
b=1
else:
print("Over the board. Try it again.")
M=Judge(B,A)
if N==1 and t==1:
Cond=1
elif MAP[B-1][A-1]!=-1:
Cond=0
print("There is already an arrow put. Try it again.")
elif M[0]+M[1]+M[2]+M[3]==-4:
Cond=0
print("There is no arrows around this square. Try it again.")
else:
Cond=1
C=-1
c=0
while c==0:
C=input("Direction (WASD):")
if C=="W" or C=="A" or C=="S" or C=="D":
c=1
D=WASD.index(C)
return [A-1,B-1,D] #[x,y,向き]を出力
def Scoring():
for i in range(len(Score)):
print("Player"+str(i+1)+" - "+Player[i]+" :"+str(Score[i]))
def Turn(N,t): # N回目, Player.tのターン
print("--------------------------------")
print("【Turn "+str(N)+", Player"+str(t)+": "+Player[t-1]+"】")
Display(MAP)
SCORE=-1
IND=Position(N,t)
X=IND[0] # O→x
Y=IND[1] #↓y の座標のとり方。配列の要素指定は List[Y][X] の形
MAP[Y][X]=(IND[2]-1)%4
x,y=X,Y
while 1<=x+1<=wide and 1<=y+1<=height and MAP[y][x]!=-1: #画面外or未開地に行くまで
MAP[y][x]+=1
if MAP[y][x]%4==0:
x+=1
elif MAP[y][x]%4==1:
y+=+1 #yを増やす→下へ
elif MAP[y][x]%4==2:
x+=-1
else:
y-=1 #yを減らす→上へ
SCORE+=1
Display(MAP)
Score[t-1]+=SCORE
print("Player "+str(t)+", "+Player[t-1]+" got ",end="")
if SCORE==0:
print("no point...")
elif SCORE==1:
print("1 point!")
else:
print(str(SCORE)+" points!")
print()
Scoring()
#=========================================
Total=wide*height #マス目の総数
turns=0
t=0
n=1
while turns<Total:
t+=1
if t>len(Player):
t=1
n+=1
Turn(n,t)
turns+=1
print("--------------------------------")
print("Game Is Over")
print()
print("RESULTS:")
Scoring()
print()
MAX=max(Score)
print("WINNER :")
for i in range(len(Player)):
if Score[i]==MAX:
print(" Player"+str(i+1)+" - "+Player[i]+" , SCORE "+str(MAX))
def文で定義された各ユーザー定義関数は
PLAYER() … プレイヤー登録、プレイヤーの人数分のスコア配列用意
SIZE() … 盤面(リスト:MAP, "Board") の大きさを決定
Board_Generation(MAP,h,w) … マップの生成
Display(M) … リストMの配列を表示
Judge(Y,X) … 盤面MAP上でプレイヤーが選択したマス MAP[Y][X]
について、その周りがどういう状況かを判定する。
Position(N,t) … 第Nターンt人目の位置を決定する
Scoring() … 各人の得点を表示
Turn(N,t) … 第Nターンt人目の行動を制御する
という状況ですね。
「#=====」などを入れた所為で少し内容量が増えている気がしますが、
合計184行のコードとなりました。まだ使い始めて間もないため、もっとコンパクトにできるんだろうなとは思っております。
Colaboratory (Colab) と調べて、実際にこのコードをコピペすることで遊ぶことができると思います。是非遊んでみてほしいです。
ゲームの特徴は以下の通りです。↓
【特徴・要素】
1.遊び方:
プレイヤー登録・盤面サイズ設定を行なってゲームをスタート
①マス目を指定して矢印を置く。
(位置を答えた後はWASDキーで答えることによって向きも決められる。)
②矢印を置いたところから、その矢印の向きに1マス進む。
③進んだ先のマスに注目し、以下の操作を反復する。
«反復の終了時点»
注目しているマスが次のいずれかの状態になる。
・注目したマスに矢印がまだ置かれていない。
・注目したマスが盤面外である。
«反復操作内容»
次の1,2を順に行う。
1. 注目したマスにある矢印を時計回りに90°回転させる。
2. 1が終わった直後、今いるマスにある矢印の方向に1マス進む。
例:2x1盤面 ◯↓ において、(1,1) (左下)に「→」(D)を置く。
→↓ 右隣の(2,2) (右下)へ移動・注目・回転
→← 左隣の(1,2) (左下)へ移動・注目・回転
↓← 下隣へ移動。盤面外なので次ターン終了
④次の人のターンへ
⑤盤面が埋まるまで回していく。盤面が埋まればゲーム終了。
2.得点のつき方:
・③の操作が終わるまでに矢印間を移動した回数ぶんだけ得点が増加する。
例:2×2盤面
◯◯ ◯◯ ◯←
◯ ↓ ↓ ← ↓ ↑
(2,2)にS (1,2)にD (2,1)にS
Player +0pt Player +2pt Player +2pt
3.矢印の置くときの注意
-位置-
・初手:自由に置くことができる
・else:次のC1,C2,C3を同時に満たすマスにのみ置くことができる。
C1: 盤面にあるマスである。
C2: 既存の矢印のあるマスの上下左右の隣のマスである。
C3: 未だに矢印の置かれていないマスである。
-向き-
・常にどの向きにも置くことができる。
そのため、やろうとすれば1ptも得ずに③の操作を終えることができる。
4.特徴
・既存の矢印を回すのではなく、盤面内に矢印を置く形式 (ご指摘あり)
・プレイヤー登録、ターン制、サイズ設定可能
・矢印を置く際に向きの選択が可能
空白(+) =-1,→ = 0,↓ = 1,← = 2,↑ = 3 として扱っている。
・矢印間を移動した回数を各人の得点として加算
・盤面の全てのマスが矢印で埋まったらゲーム終了 (ご助言あり)
【あとがき】
いま現在まだ Twitter 上でも気づかれていないため、これからどういう反応をされるのだろうと思っております。
普通なら「こういうの作ってみました」みたいな報告をするようなテンションでやるべきことを、こんな「俺が作ったぞ!」的な、本題を読んでいるうちに「何こいつ偉そうに自分語りして」みたいに自分で感じてしまったnoteがいつの間にかできていました。
今年1年はもう早々から受験勉強をしなければならないという心持ちでいなければならない気がします。
できる時間がもしあれば投稿準備を進めていこうと思います。
とりあえず、これからも一年、よろしくお願いします。
【クレジット・スペシャルサンクス】
・数学デー さん (@sugaku_day)
・キグロ さん (@kiguro_masanao)
・矢印問題原案者:さくB さん (@sakusaku858)
・数学デー参加者の皆さん