XENOをpythonで実装

XENOの動画、めっちゃくちゃ面白いですよね。
普段はrailsを書いてるのですが、たまにはpythonの練習を兼ねて今話題のXENOをpythonで実装してみました。

そのうち機械学習のネタに使ってみたりyoutube投稿してみたい。

mannual_flagをTrueにすればテキトーロジックのCPU戦が遊べます。




```python:Xeno.py
import random

class Xeno():
	def __init__(self):
		self.winner = "none"

	class Player():
		def __init__(self,teban):
			self.hand = []
			self.field = []
			self.predict_flag = False
			self.defence_flag = False
			self.mannual_flag = False
			if teban == 0:
				self.teban="sente"
			elif teban ==1:
				self.teban="gote"

	def set_deck(self):
		card_list=[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,10]
		random.shuffle(card_list)
		self.deck = card_list
		#転生札処理
		self.reincarnation_card = self.deck.pop(-1)

	#山札から１枚ドロー
	def draw_top_card(self):
		if len(self.deck) > 0:
			self.drawed_card=self.deck.pop(0)
			return self.drawed_card

	#賢者実行 デッキから３枚見て１枚をドローカードにしデッキをシャッフル
	def do_predict(self):
		if len(self.deck) > 2:
			options = self.deck[0:3]
			if self.player.mannual_flag ==True:
				print("賢者の効果発動")
				print(options)
				print("選択するカードを入力してください")
				ind = options.index(self.inputnumber())
			else:
				ind = options.index(options[1])
			selected_card = self.deck.pop(ind)
			random.shuffle(self.deck)
			self.drawed_card = selected_card
			return self.drawed_card

	#マッチセットアップ
	def match_initialize(self):
		self.sente = self.Player(0)
		self.gote = self.Player(1)

		#山札、転生札を作成し、各プレイヤー１枚ずつドロー
		self.set_deck()
		self.sente.hand.append(self.draw_top_card())
		self.gote.hand.append(self.draw_top_card())

	#ドローからターンエンドまでのシークエンス
	def play_turn(self,_player):
		if _player == self.sente:
			self.player = self.sente
			self.o_player = self.gote
		else:
			self.player = self.gote
			self.o_player = self.sente

		if self.player.predict_flag == True and len(self.deck) > 2:
			self.drawed_card = self.do_predict()
			self.player.predict_flag = False
		else:
			self.drawed_card = self.draw_top_card()


		#ドローカードを手札に追加
		self.player.hand.append(self.drawed_card)
	
		if self.player.mannual_flag ==True:
			print("drawed card:",self.drawed_card)
			print(self.player.teban, self.player.hand)

		#手札からカードを１枚場に出す
		self.play_card = self.decide_play_card()

		#手札から出したカードを削除
		self.player.hand.remove(self.play_card)
		#場に追加
		self.player.field.append(self.play_card)
		# print("player hand:", self.player.hand)

		#出したカードの効果をプレイ 
		if self.o_player.defence_flag != True or self.play_card == 7 or self.play_card == 4:
			self.play_effect()
		#相手が守護を出していた場合、フラグリセットのみ
		else:
			self.o_player.defence_flag = False

		print("player field:", self.player.field)
		print("o_player field:", self.o_player.field)

		#ターン終了時、デッキがなくなっていた場合、手札バトル
		print("deck maisuu:", len(self.deck))
		if len(self.deck) == 0:
			self.battle()

	#最終決戦
	def battle(self):
		print("battle!!!!!!!!!!!!!")
		print("sente:", self.sente.hand[0])
		print("gote:", self.gote.hand[0])
		if self.sente.hand[0] > self.gote.hand[0]:
			self.winner = self.sente.teban
		elif self.gote.hand[0]>self.sente.hand[0]:
			self.winner = self.gote.teban
		else:
			self.winner = "DRAW"

	def decide_play_card(self):
		if self.player.mannual_flag== True:
			print("プレイするカード番号を入力してください")
			return self.inputnumber()
		else:
			if self.player.hand.count(10) != 1:
				# random.shuffle(self.player.hand)
				self.player.hand.sort()
				return self.player.hand[0]
			else:
				self.player.hand.sort()
				return self.player.hand[0]

	def play_effect(self):
		pl = self.play_card
		if pl == 1:
			print("play:", pl)
			self.revolution()
		elif pl ==2:
			print("play:", pl)
			self.detect_number()
		elif pl == 3:
			print("play:", pl)
			self.publicate()
		elif pl ==4:
			print("play:", pl)
			self.skip_effect()
		elif pl ==5:
			print("play:", pl)
			self.hide_draw_and_drop()
		elif pl == 6:
			print("play:", pl)
			self.battle_novice()
		elif pl == 7:
			print("play:", pl)
			self.predict()
		elif pl == 8:
			print("play:", pl)
			self.exchange()
		elif pl == 9:
			print("play:", pl)
			self.public_draw_and_drop()

	def inputnumber(self):
		if self.player.mannual_flag==True:
			s = int(input())
			return s
		else:
			return 1

	#1 少年
	def revolution(self):
		all_field = self.sente.field + self.gote.field
		#フィールド上にすでに１が出ていた場合のみ公開処刑
		print(all_field)
		if all_field.count(1) > 1:
			self.public_draw_and_drop()

	#2 兵士
	def detect_number(self):
		detection_number = 3
		if self.player.mannual_flag == True:
			print("兵士の効果発動")
			print("相手の手札予想を入力してください")
			detection_number = self.inputnumber()
		else:
			detection_number = 5

		if self.o_player.hand[0] == detection_number:
			self.winner = self.player.teban

        #3 占い師
	def publicate(self):
		print(self.o_player.hand)
	
	#4 守護
	def skip_effect(self):
		self.player.defence_flag = True

	#5 死神
	def hide_draw_and_drop(self):
		if len(self.deck)>0:
			#相手は１枚ドローし手札シャッフル
			self.draw_card = self.draw_top_card()
			self.o_player.hand.append(self.draw_card)
			random.shuffle(self.o_player.hand)
			if self.player.mannual_flag == True:
				print("捨てさせる手札の位置を選んでください　0:左 1:右")
				inp = self.inputnumber()
			else:
				inp = 0

			#0番目カードをドロップ
			drop_target_card = self.o_player.hand.pop(inp)
			self.o_player.field.append(drop_target_card)

			#targetが10だった場合、転生処理
			if drop_target_card == 10:
				self.reincarnate()

	#6 貴族
	def battle_novice(self):
		self.battle()

	#7 賢者
	def predict(self):
		self.player.predict_flag = True

	#8 聖霊
	def exchange(self):
		tmp = self.player.hand[0]
		self.player.hand[0] = self.o_player.hand[0]
		self.o_player.hand[0] = tmp

	#9 皇帝
	def public_draw_and_drop(self):
		if len(self.deck) > 0:
			self.o_player.hand.append(self.draw_top_card())
			#手札公開
			if self.player.mannual_flag ==True:
				print("相手の手札はこちら")
				print(self.o_player.hand)
				#ターゲット選択
				print("select target number:")
				drop_target = self.inputnumber()
				ind = self.o_player.hand.index(drop_target)
			else:
				ind = 0

			drop_target_card = self.o_player.hand.pop(ind)
			self.o_player.field.append(drop_target_card)

			#皇帝のカード(9)がプレイされたかつターゲットカードが英雄(10)だった場合はplayer勝利
			if drop_target_card == 10:
				if self.play_card == 9:
					self.winner = self.player.teban
				elif self.play_card == 1:
					self.reincarnate()

	#10 英雄
	def reincarnate(self):
		#手札を捨てる 
		self.o_player.field.append(self.o_player.hand.pop(0))
		#転生札をドロー
		self.o_player.hand.append(self.reincarnation_card)


def main():
	winner_list = []
	win_process =[]
	for i in range(1,1000):
		xeno = Xeno()
		xeno.match_initialize()
		xeno.sente.mannual_flag = False
		xeno.gote.mannual_flag = False

		while True:
			print("=======================")
			if xeno.winner == "none":
				xeno.play_turn(xeno.sente)
			else:
				break
			print("=======================")
			if xeno.winner == "none":
				xeno.play_turn(xeno.gote)
			else:
				break

		print("winner :", xeno.winner)
		winner_list.append(xeno.winner)


	print("gote",winner_list.count("gote"))
	print("sente",winner_list.count("sente"))
	print("Draw",winner_list.count("DRAW"))

if __name__ == '__main__':
    main()

```

ちなみにロジックテキトーですが、10000試合の結果
gote 4543
sente 4967
Draw 489

6,10の組み合わせの初手勝ちがあり得る分、やはり先手のほうが有利ですね。
後、守護のカードは実質「相手に手札を選んで捨てさせる + 相手のターンスキップ」と同義なのでかなり強いアタッカーになってる気がします。