読者です 読者をやめる 読者になる 読者になる

HPO機密日誌

自己をならふといふは、自己をわするるなり。

繰り返し囚人ゲーム ソース

#!/usr/bin/env python
#coding:utf-8

"""
「「移動付繰り返し囚人ゲーム・シミュレーション」」

<<世界>>

NUM_COL 横の数
NUM_ROW 縦の数
世界 NUM_COL × NUM_ROWのテーブル。pythonの入れ子リスト構造で表現。
self.field 今回の世界

<<生物>>

生物のリスト構造
   0=x 1=y 2=asset 3=hist 4=win or loose last time, 5=strategy
 lives = [x:, y:, assets10, hist:10, win/loose:1/-1, strategy:0-4]  生物のリスト、ゲーム内の生物の辞書構造と標準値
 assetは、自分の資産。エネルギー残量。
 histは、0だと死んでる。1以上だと生きてる。
 win/loose -1だと負けた(裏切られた)後、1だと勝った後。
strategy @:お人好し、 1:裏切り者、 2:経歴重視(histが小さくなると裏切る)、 3:前回勝てば同意、負ければ裏切り、 4:ランダム

<<囚人ゲーム>>
gamet_tabel 二つのセルの間の演算(対戦)の結果それぞれのセルが受け取る値。囚人のジレンマゲームのテーブル。

戦略は同意か、裏切りの二つ。結果は以下の通り。

	相手  同意=0      裏切=1
自分
 同意  1/1		-6 / 0
 裏切	0/ -6	   -3/-3

"""

import pygame
from pygame.locals import *
import random
import sys


SCR_RECT = Rect(0, 0, 800, 600)  # スクリーンサイズ
CS = 10  # セルのサイズ
NUM_ROW = SCR_RECT.height / CS   # フィールドの行数
NUM_COL = SCR_RECT.width / CS  # フィールドの列数
DEAD, ALIVE = -1, 0  # セルの生死定数
NUM_LIVES = 100 # 生物の数
NUM_STRATEGY = 4 # 戦略の数 -1
# 生物のゲームの報酬表 双方信頼、1P信頼ー2P裏切、 1P裏切ー2P信頼、 双方裏切
TABLE_GAME = [[[1, 1,1,1],[-6,0,-1,1]],[[0,-6,1,-1],[-3,-3,-1,-1]]] 
TABLE_MOVE = [[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]] # 8方向への移動


class PrisonerGame:
	def __init__(self):
		global SCR_RECT, CS, NUM_ROW, NUM_COL, DEAD, ALIVE, NUM_LIVES, TABLE_GAME, TABLE_MOVE, table_rand
		pygame.init()
		screen = pygame.display.set_mode(SCR_RECT.size)
		pygame.display.set_caption(u"Prisoner Game")
		self.font = pygame.font.SysFont(None, 16)
		# NUM_ROW x NUM_COLサイズのフィールド(2次元リスト) &
		#生物の生成 dict key : x,y,assets, hist, win/loose, strategy
		self.num_lives_c = NUM_LIVES
		self.std_assets=10
		self.std_hist=10
		self.std_wl=0
		self.std_strategy=0
		#世界と生物の初期化(ランダム)
		self.generation = 0  # 世代数
		self.run = False  # シミュレーション実行中か?
		self.cursor = [NUM_COL/2, NUM_ROW/2]  # カーソルの位置
		# ライフゲームを初期化
		self.clear()
		self.table_rand() 
		# メインループ
		clock = pygame.time.Clock()
		while True:
			clock.tick(60)
			self.update()
			self.draw(screen)
			pygame.display.update()
			for event in pygame.event.get():
				if event.type == QUIT:
					pygame.quit()
					sys.exit()
				elif event.type == KEYDOWN:
					if event.key == K_ESCAPE:
						pygame.quit()
						sys.exit()
					# 矢印キーでカーソルを移動
					elif event.key == K_LEFT:
						self.cursor[0] -= 1
						if self.cursor[0] < 0: self.cursor[0] = 0
					elif event.key == K_RIGHT:
						self.cursor[0] += 1
						if self.cursor[0] > NUM_COL-1: self.cursor[0] = NUM_COL-1
					elif event.key == K_UP:
						self.cursor[1] -= 1
						if self.cursor[1] < 0: self.cursor[1] = 0
					elif event.key == K_DOWN:
						self.cursor[1] += 1
						if self.cursor[1] > NUM_ROW-1: self.cursor[1] = NUM_ROW-1
					# スペースキーでカーソルのセルを反転
					elif event.key == K_SPACE:
						x, y = self.cursor
						self.add(x,y)
					# sキーでシミュレーション開始
					elif event.key == K_s:
						self.run = not self.run
					# nキーで1世代だけ進める
					elif event.key == K_n:
						self.step()
					# cキーでクリア
					elif event.key == K_c:
						self.clear()
					# rキーでランダムに生きているセルを追加
					elif event.key == K_r:
						self.clear()
						self.table_rand()
				elif event.type == MOUSEBUTTONDOWN and event.button == 1:
					# 左ボタンクリックでセルを反転
					px, py = event.pos
					x, y = px/CS, py/CS
					self.cursor = [x, y]
					self.add(x,y)
	def add(self,x,y):
		global NUM_LIVES
		if self.field[y][x] == DEAD:
			self.field[y][x] = self.num_lives_c
			self.lives.append({"x":x,"y":y,"assets":self.std_assets,"hist":self.std_hist,"wl":self.std_wl,"strategy":random.randint(0, NUM_STRATEGY)})
			self.num_lives_c = self.num_lives_c + 1
		elif self.field[y][x] >= ALIVE:
			self.lives[self.field[y][x]]["assets"] = 0
			self.field[y][x] = DEAD
	def clear(self):
		"""ゲームを初期化"""
		self.generation = 0
		self.num_lives_c = NUM_LIVES
		self.field = [[DEAD for x in range(NUM_COL)] for y in range(NUM_ROW)]
		self.lives=[]
		self.num_lives_c = 0
	def table_rand(self): # "lives"の値を乱数で設定し、世界に配置する
		self.num_lives_c = NUM_LIVES
		self.lives = [{"x":0,"y":0,"assets":self.std_assets,"hist":self.std_hist,"wl":self.std_wl,"strategy":self.std_strategy} for x in range(self.num_lives_c)] 
		for i in range(self.num_lives_c):
			finish_flag=0
			while finish_flag==0 :
				x=random.randint(1, NUM_COL - 1)
				y=random.randint(1, NUM_ROW - 1)
				if self.field[y][x] == DEAD :
					self.field[y][x]=i
					self.lives[i]["x"]=x
					self.lives[i]["y"]=y
					self.lives[i]["strategy"]=random.randint(0, NUM_STRATEGY)
					finish_flag=1
	def update(self):
		"""フィールドを更新"""
		if self.run:
			self.step()  # 1世代進める
	def step(self):
		for j in range(len(self.lives)):
			if self.lives[j]["assets"] > 0 :
				a_round = self.around(self.lives[j]["x"], self.lives[j]["y"])
				if a_round >= 0 :
					self.table_trade(j,a_round)
				else :
					self.field_move(j, random.randint(0,7))
		self.generation += 1
		print "Generation %d th " % self.generation
	def field_move(self, live_num, direction) : # 方向は0から7で表す、世界の外、重なりのチェック、周りにない時だけ動ける
		x = self.lives[live_num]["x"]
		y = self.lives[live_num]["y"]
		new_x = x+TABLE_MOVE[direction][0]
		new_y = y+TABLE_MOVE[direction][1]
		a_round=self.around(x,y)
		if not (a_round >= 0 or new_x < 1 or new_y < 1 or new_x > NUM_COL-1 or new_y > NUM_ROW - 1) :
			self.field[y][x] = DEAD
			self.field[new_y][new_x]=live_num
			self.lives[live_num]["x"]=new_x
			self.lives[live_num]["y"]=new_y
	def table_trade(self, num1, num2): # 2つの番号(生物)を指定してゲーム(trade)させる。
		assets_num1 = self.lives[num1]["assets"]
		assets_num2 = self.lives[num2]["assets"]
		decision_num1 = self.trade_strategy(num1)
		decision_num2 = self.trade_strategy(num2)
		self.die_or_suv(num1, decision_num1, TABLE_GAME[decision_num1][decision_num2][0], TABLE_GAME[decision_num1][decision_num2][2])
		self.die_or_suv(num2, decision_num2, TABLE_GAME[decision_num1][decision_num2][1], TABLE_GAME[decision_num1][decision_num2][3])
		print
	def trade_strategy(self, num): # 思考ルーチン、裏切る時の条件だけ列挙。それ以外は「同意」
		hist_status=self.lives[num]["hist"]
		last_w_l = self.lives[num]["wl"]
		strategy_type=self.lives[num]["strategy"]
		if strategy_type==1 :#裏切り者
			return 1
		elif strategy_type == 2 and hist_status <= 7 :# 負けつづけた「歴史」
				return 1
		elif strategy_type == 3 and last_w_l == -1 : # 負けた「前回」
				return 1  
		elif strategy_type == 4 and random.random() < .3 : # ランダム
				return 1  
		else :
			return 0 #お人好しと裏切らない意思決定の場合
	def die_or_suv(self, num, decision, trade, wl) :
		if (self.lives[num]["assets"] + trade) <= ALIVE :
			self.dead_lives(num)
		else:
			self.lives[num]["assets"] = self.lives[num]["assets"] + trade
			self.lives[num]["hist"] = self.lives[num]["hist"] + wl
			self.lives[num]["wl"] = wl
			a_print = "num: %d x: %d y: %d " % (num, self.lives[num]["x"], self.lives[num]["y"])
			a_print = a_print + "asset: %d hist: %d w/l %d strategy: %d " % (self.lives[num]["assets"], self.lives[num]["hist"], self.lives[num]["wl"], self.lives[num]["strategy"])
			a_print = a_print + "decision: %d result %d" % (decision, trade)
			print a_print
	def dead_lives(self,num): # 死んだときの処理
		self.field[self.lives[num]["y"]][self.lives[num]["x"]]=DEAD
		self.lives[num]["assets"] = 0
	def draw(self, screen):
		"""フィールドを描画"""
		# セルを描画
		for y in range(NUM_ROW):
			for x in range(NUM_COL): 
				s_num = self.field[y][x]
				if  s_num > ALIVE:
#					if s_num == 0 :
#						print s_num
					s_color =  self.lives[s_num]["assets"]
					s_strategy = self.lives[s_num]["strategy"]
					if s_color > 255 :
						s_color = 255
					if s_color < 255 :
						s_color = 0
					if s_strategy == 0:
						pygame.draw.rect(screen, (int(s_color/2), 0, 255), Rect(x*CS,y*CS,CS,CS)) # お人好し 緑→紫
					elif s_strategy == 1 :
						pygame.draw.rect(screen, (255, 0 , int(s_color/2)), Rect(x*CS,y*CS,CS,CS)) # 裏切者 赤→赤緑
					elif s_strategy == 2 :
						pygame.draw.rect(screen, (0, 255, int(s_color/2)), Rect(x*CS,y*CS,CS,CS)) # 過去 青→深緑
					elif s_strategy == 3 :
						pygame.draw.rect(screen, (int(s_color/4), 125, 125), Rect(x*CS,y*CS,CS,CS)) # 前回 薄青→灰色
					else :
						pygame.draw.rect(screen, (int(s_color/4), 0, 180), Rect(x*CS,y*CS,CS,CS)) # ランダム 薄緑→ 薄紫
				elif s_num == DEAD:
					pygame.draw.rect(screen, (0,0,0), Rect(x*CS,y*CS,CS,CS))
				pygame.draw.rect(screen, (50,50,50), Rect(x*CS,y*CS,CS,CS), 1)  # グリッド
		# 中心線を描く
		pygame.draw.line(screen, (255,0,0), (0,SCR_RECT.height/2), (SCR_RECT.width,SCR_RECT.height/2))
		pygame.draw.line(screen, (255,0,0), (SCR_RECT.width/2,0), (SCR_RECT.width/2,SCR_RECT.height))
		# カーソルを描画
		pygame.draw.rect(screen, (0,0,255), Rect(self.cursor[0]*CS,self.cursor[1]*CS,CS,CS), 1)
		# ゲーム情報を描画
		screen.blit(self.font.render("generation:%d" % self.generation, True, (0,255,0)), (0,0))
		screen.blit(self.font.render("space : birth/kill", True, (0,255,0)), (0,12))
		screen.blit(self.font.render("s : start/stop", True, (0,255,0)), (0,24))
		screen.blit(self.font.render("n : next", True, (0,255,0)), (0,36))
		screen.blit(self.font.render("r : random", True, (0,255,0)), (0,48))
	def around(self,x, y):
		"""(x,y)の周囲8マスの生きているセルの数を返す"""
			#def around(x,y): # 世界の位置をx,yで指定して周囲の存在をたしかめる。
			#print "x-upper ",len(table[1]),"   y-upper",len(table) 
		if x < 2 or x > (NUM_COL - 2) or y < 2 or y > (NUM_ROW - 2) :
			return DEAD
		else:
			for i in [-1, 0, 1]:
				for j in [-1, 0, 1]:
					if not (i==0 and j==0):
						a = self.field[y+j][x+i]
						if  a > ALIVE:
							return a
			return DEAD

if __name__ == "__main__":
	PrisonerGame()