Infinite Prisoner's Infilemma
Introduction
In this king-of-the-hill challenge, you are going to
write a program that will play the game of the
Infinite Prisoner's Infilemma.
This game is based on the classic game of the name
Infinite Prisoner's Dilemma
.
The original inspiration for this game came from this
game by
Nicky Case.
By default we will include the sample bots from Nicky
Case's game. You can also add your own bots.
How to Play
Given a list of bot models [1,2,3,4,5]
:
- First, multiply the list by 10 to get 10 bots of each model.
- Then, battle every two bots together.
During a battle there will be 10 rounds. Each round:
- Both players will enter
x
dollars into a machine, where
x
is non-negative (not neccessarily an integer) and less
than or equal to the amount of money they have. They start
with $100.
- The other player will recieve 1.1 times the amount of money.
Default Bots
Default bots are:
Copycat, which starts by cooperating with 1/3 of his money,
and then cooperates with the same ammount of money as the
ammount of money you cooperated last turn. (Unless he doesn't
have enough money, which he will use all of them).
AlwaysCheat, which, always cheats by giving no money. DUH!
AlwaysCooperate, which, always cooperates by giving 1/2 of his
money.
Grudger, which starts by cooperating with 1/3 of his money but
gives no more money if you have ever cheated and give less than
$10.
Detective, which cooperates $20, $5, $10, but if the opponent
always gave at least $50, will act like an AlwaysCheat, else
will act like a Copycat.
Copykitten, which is similar to Copycat, but cooperates with
the same ammount of money as the max of the opponent's last
two turns.
Simpleton, which starts by cooperating 1/2 of his money, then
at every turn, if the opponent gave less than $20, switch a
state between 1/2 of his money and none.
Random, which give, randomly, 2/3 to none of his money.
Instructions
Your bot should be a class. It will inherit from this Bot class:
class Bot:
def __init__(self):
self.money = 100
self.information = {}
def log(self, bot, money):
if bot not in self.information:
self.information[bot] = [money]
else:
self.information[bot] += [money]
def change_money(self, money):
self.money += money
def choice(self, bot):
pass # override in subclass
def name(self):
pass # should return the name of the bot
def get_choice(self, bot):
choice = self.choice(bot)
assert 0 <= choice <= self.money, \
f"Invalid choice {choice}, bot money {self.money}" # check if choice is valid
return choice
You may not use any other libraries other than math
and random
. (If you have any requests, let me know.
I mainly want to ban the use of inspect
, sys
, and os
.)
You may also not override any methods other than choice
and
name
.
You may not set self.money
.
(Else, you can come up with self.money = 10000, etc.)
The following is a sample bot:
from main import Bot # <-- import the Bot class
from random import random as r # <-- import the random function
class TestBot(Bot): # <-- create a class called TestBot
# <-- that inherits from Bot
def choice(self, bot): # <-- override the choice method
return self.money*r()/10 # random number between 0 and
# 10% of the bot's money
def name(self): # <-- override the name method
return "randombot" # <-- return the name of the bot
bot = TestBot # <-- IMPORTANT: create a variable called bot
The Information Log
Well, sometimes you want to know what the opponent did.
Thus, sometimes you need the "information log", called
via self.information
. Remember the bot
argument
that is passed in the choice
method? That is your
opponent. To get the information log of your opponent,
use the following:
self.information[bot] # <-- get the information log of the opponent
What does that information look like? Well, it's like
[[Y1,O1],[Y2,O2],...]
. Y1
is what you gave the
opponent in round 1. The rest is fairly easy to infer.
Controller Script
# This is the controller script for the Infinite Prisoner's Infilemma King of the Hill game.
bots = ['testbot','randombot'] # Will change.
# to make it fold-able.
if True:
from rich.console import Console
from rich.table import Table
console = Console()
with open('log.txt','w') as f: pass
def print(*args, **kwargs): console.print(*args, **kwargs)
def log(*text):
with open('log.txt','a') as f:
f.write(' '.join(list(map(str,text)))+'\n')
def play(Bot1, Bot2):
log(Bot1.name(), 'vs', Bot2.name())
b1_choice, b2_choice = Bot1.get_choice(Bot2), Bot2.get_choice(Bot1)
Bot1.change_money(-b1_choice)
Bot2.change_money(-b2_choice)
log(b1_choice, b2_choice)
Bot1.change_money(1.001*b2_choice)
Bot2.change_money(1.001*b1_choice)
log(Bot1.name(), 'has', Bot1.money, 'dollars')
log(Bot2.name(), 'has', Bot2.money, 'dollars')
Bot1.log(Bot2, b1_choice, b2_choice)
Bot2.log(Bot1, b2_choice, b1_choice)
log('\n\n')
class Bot:
def __init__(self):
self.money = 100
self.information = {}
def log(self, bot, money):
if bot not in self.information:
self.information[bot] = [money]
else:
self.information[bot] += [money]
def change_money(self, money):
self.money += money
def choice(self, bot):
pass # override in subclass
def name(self):
pass # should return the name of the bot
def get_choice(self, bot):
choice = self.choice(bot)
assert 0 <= choice <= self.money, \
f"Invalid choice {choice}, bot money {self.money}" # check if choice is valid
return choice
def leaderboard(Bots):
table = Table(show_header=True, header_style="bold #00ffff")
table.add_column("Bot", style="bold #ffff00") # yellow
table.add_column("Money", justify="right", style="#00ff00") # green
for bot in sorted(Bots, key=lambda x:-x.money):
table.add_row(bot.name()+' ',
f" ${str(round(bot.money,2)) if str(round(bot.money,2))[::-1][1] != '.' else str(round(bot.money,2))+'0'}")
print(table)
def main():
print('\n\n\n')
Bots = []
for bot in bots:
for i in range(10): Bots.append(__import__(bot).bot())
for Bot1 in Bots:
for Bot2 in Bots:
if Bot1 is not Bot2:
for i in range(10): play(Bot1,Bot2)
leaderboard(Bots)
print('\n\n\n')
if __name__ == '__main__':
main()