HPO機密日誌

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

繰り返し囚人のジレンマゲーム・シミュレーション

先日、「徳の起源」がらみで繰り返し囚人ゲームをやったような口をたたいてしまった。実際には、ライフゲームの延長で移動しないグリッドで囚人ゲーム的なことをやったにすぎない。それなのに、山口浩さんからお言葉をいただいてしまった。

徳の起源―他人をおもいやる遺伝子

徳の起源―他人をおもいやる遺伝子

@hidekih 実際にやってみたのですね。面白いです。あれもいろいろな前提条件によって結果が異なったりしますので、そのまま現実にもってくるのは危険ですが、直感に沿ってますよね。

http://twitter.com/HYamaguchi/status/408784220085956608

で、pythonをほぼ最初から勉強しなおして、なんとか移動する繰り返す囚人ゲームシミュレーションを作った。山口さんのご指摘どうり、「前提条件」をよく整備していないので、断言はできないが、お互いに協力しあった時の報酬の値で大きく結果は違う。お互いに相手を信頼しあっても報酬が少なければ、ごくごく少数しか生き残れず、かつ裏切り者が生き残る。ほんの少し、お互いが協力しあった時の報酬と大きくするだけで、協力する方が有利なようだ。ま、とにかく書いたというレベル。平成26年の正月の記念ということでさらしておく。

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

<<世界>>

bcolum 横の数
braw 縦の数
世界 bcolum × brawのテーブル。pythonの入れ子リスト構造で表現。
world 今回の世界
table 「世界」を関数に渡す時の内部変数

<<生物>>

生物のリスト構造
   0=x 1=y 2=asset 3=life 4=strategy
 lives = [0,0,100,1,0]*number_life # 生物のリスト、ゲーム内の生物の辞書構造と標準値
 x, y は位置。ちなみ、teble[y][x]となる。
 assetは、自分の資産。エネルギー残量。
 lifeは、0だと死んでる。1以上だと生きてる。1だと負けた(裏切られた)後、2だと勝った後。つーか、途中から勝ち数の記録に変更。


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

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

	相手  同意=0    裏切=1
自分
 同意  10/10        -60 / 0
 裏切    0/ -60       -30/-30
"""

import random

def init_and_main() :
	global 	bcolum, braw, world, lives, move_table, number_lives,game_table
	bcolum = 10
	braw = 10
	world=[] # ゲームの番
	number_lives=30 # 生物の数
	game_table=[[[10,10,1,1],[-60,0,-1,1]],[[0,-60,1,-1],[-30,-30,-1,-1]]] # ゲームの結果表
	move_table=[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]] # 8方向への移動
	world=table_int(bcolum,braw) #世界の生成
	lives=life_int(number_lives) #生物の生成
	table_rand(world, lives) #世界と生物の初期化(ランダム)
	print_table(world) #プリント 世界
	print_lives(lives) #プリント 生物
#	test_trade(world, lives)
	move_and_trade(world, lives, 10)
	print_table(world)
	print_lives(lives)


def table_int(col, raw): # 初期化。新しい世界を作る。出力を代入することで初期化する。
	table=[-1]*(col + 1)
	for i in range(col + 1):
		table[i] = [-1]*(raw + 1)
	return table


def life_int(num): # 生物のリストの初期化。出力を変数に代入して生物を作る。値は全て標準値のまま。
	live=[0]*num
	for i in range(num):
		live[i]=[0,0,100,4,0]
	return live


def table_rand (table, local_lives): # "lives"の値を乱数で設定し、世界に配置する
	for i in range(len(local_lives)):
		finish_flag=0
		while finish_flag==0 :
			x=random.randint(1,len(table[1])-1)
			y=random.randint(1,len(table)-1)
#			print i,finish_flag,x,y
			if table[y][x]==-1 :
				table[y][x]=i
				local_lives[i][0]=x
				local_lives[i][1]=y
				local_lives[i][4]=random.randint(0,2)
#				print local_lives[i][0], local_lives[i][1]
				finish_flag=1


def table_move(table, local_lives, life_num, direction) : # 方向は0から7で表す、世界の外、重なりのチェック、周りにない時だけ動ける
	table_border_x=len(table[0])
	table_border_y=len(table)
	x=local_lives[life_num][0]
	y=local_lives[life_num][1]
	new_x=x+move_table[direction][0]
	new_y=y+move_table[direction][1]
	a_round=table_round(table,x,y)
	if a_round >= 0 or new_x < 1 or new_y < 1 or new_x > table_border_x-1 or new_y > table_border_y-1 :
		new_x=x
		new_y=y
	table[y][x]=-1
	table[new_y][new_x]=life_num
	local_lives[life_num][0]=new_x
	local_lives[life_num][1]=new_y


def table_round(table,x,y): # 世界の位置をx,yで指定して周囲の存在をたしかめる。
#	print "x-upper ",len(table[1]),"   y-upper",len(table) 
	if (x < 2) or (x > (len(table[1]))- 2) or (y < 2) or (y > (len(table) - 2)) :
		return -1
	else:
		if count_lives(table,x-1,y-1) >= 0:
			return count_lives(table,x-1,y-1)
		elif count_lives(table,x,y-1) >=0 :
			return count_lives(table,x,y-1)
		elif count_lives(table,x+1,y-1) >= 0 :
			return count_lives(table,x+1,y-1)
		elif count_lives(table,x-1,y) >= 0 :
			return count_lives(table,x-1,y)
		elif count_lives(table,x+1,y) >= 0 :
			return count_lives(table,x+1,y)
		elif count_lives(table,x-1,y+1) >= 0 :
			return count_lives(table,x-1,y+1)
		elif count_lives(table,x,y+1) >= 0 :
			return count_lives(table,x,y+1)
		elif count_lives(table,x+1,y+1) >= 0 :
			return count_lives(table,x+1,y+1)
		else :
			return -1


def count_lives(table,x,y):
#	print x,y
	a = table[y][x]
	if a == -1:
		return -1
	else:
		return a


def print_lives(local_lives): #生物の属性の表示
	for i in range(len(local_lives)) :
		print "num: %d x: %d y: %d asset: %d life: %d strategy: %d" % (i, local_lives[i][0], local_lives[i][1], local_lives[i][2], local_lives[i][3], local_lives[i][4])


def print_table (b): # 世界の表示
	print "-"*len(b[1])*4
	for i in range(len(b)):
		for j in range(len(b[i])):
			if b[i][j]==-1 :
				print "|  ",
			else :
				print "|%2d" % b[i][j],
		print "|"
		print "-"*len(b[1])*4


def table_trade(table, local_lives, num1, num2): # 2つの番号(生物)を指定してゲーム(trade)させる。
	assets_num1 = local_lives[num1][2]
	assets_num2 = local_lives[num2][2]
	decision_num1 = trade_strategy(local_lives, num1)
	decision_num2 = trade_strategy(local_lives, num2)
	trade_num1 = game_table[decision_num1][decision_num2][0]
	trade_num2 = game_table[decision_num1][decision_num2][1]
	if assets_num1 + trade_num1 <0 :
		dead_lives(table, local_lives, num1)
	else:
		local_lives[num1][2] = assets_num1 + trade_num1
		local_lives[num1][3] = local_lives[num1][3] + game_table[decision_num1][decision_num2][2]
	if assets_num2 + trade_num2 <0 :
		dead_lives(table, local_lives, num2)
	else:
		local_lives[num2][2] = assets_num2 + trade_num2
		local_lives[num2][3] = local_lives[num2][3] + game_table[decision_num1][decision_num2][3]
	print assets_num1,assets_num2,decision_num1,decision_num2,trade_num1,trade_num2, game_table[decision_num1][decision_num2][2],game_table[decision_num1][decision_num2][3]
	print "num: %d x: %d y: %d asset: %d life: %d strategy: %d" % (num1, local_lives[num1][0], local_lives[num1][1], local_lives[num1][2], local_lives[num1][3], local_lives[num1][4])
	print "num: %d x: %d y: %d asset: %d life: %d strategy: %d" % (num2, local_lives[num2][0], local_lives[num2][1], local_lives[num2][2], local_lives[num2][3], local_lives[num2][4])
	print


def trade_strategy(local_lives, num): # 思考ルーチン、あとでよく考える
	strategy_type=local_lives[num][4]
	life_status=local_lives[num][3]
	if strategy_type==1 :#裏切り者
		return 1
	elif strategy_type==2 and life_status <= 3 :#負けた後の「考える人」
		return 1
	elif strategy_type==2 and life_status >= 4 :#勝った後の「考える人」
		return 0
	else :
		return 0 #お人好し


def dead_lives(table, local_lives,num): # 死んだときの処理
	local_lives[num][3]=0
	table[local_lives[num][1]][local_lives[num][0]]=-1


def test_trade(table, local_lives):
	for i in range(100):
		not_same=0
		while not_same==0:
			a=random.randint(0,number_lives-1)
			b=random.randint(0,number_lives-1)
			if a!=b :
				not_same = 1
		table_trade(table, local_lives,a,b)
	print_table(table)
	print_lives(local_lives)


def move_and_trade(table, local_lives, count):
	for i in range(count):
		for j in range(len(local_lives)):
			if local_lives[j][3] > 0 :
#				print "x",local_lives[j][0],"  y",local_lives[j][1]
				a_round = table_round(table, local_lives[j][0],local_lives[j][1])
#				print a_round
				if a_round != j :
					if a_round >= 0 :
						table_trade(table, local_lives,j,a_round)
					else :
						table_move(table, local_lives, j, random.randint(0,7))
#						print j, local_lives[j][0],local_lives[j][1]