bic-venom-lethal

AuthorMicah
Submission date2011-06-14 09:03:27.626680
Rating7486
Matches played5224
Win rate74.04

Use rpsrunner.py to play unranked matches on your computer.

Source code:

import random
from collections import defaultdict

class TieredMarkov:
  def __init__(self, order, dampen):
    self.order = order
    self.dampen = dampen
    self.chains = [NMarkov(i+1) for i in range(0,order)]

  def dump_data(self):
    return [chain.dump_data() for chain in self.chains]
  
  def get_counts(self, state):
    counts = [self.chains[i].get_counts(state) for i in range(0,self.order)]
    keys = set(key for d in counts for key in d.keys())
    merged = dict((key, sum(counts[i].get(key,0) * self.dampen ** i for i in range(len(counts)))) for key in keys)
    return merged
  
  def add_point(self, state, next, weight=1):
    [chain.add_point(state, next, weight) for chain in self.chains]

class NMarkov:
  def __init__(self, order):
    self.order = order
    self.state_counts_map = {}

  def dump_data(self):
    return self.state_counts_map

  def get_key(self, state):
    return str(state[-self.order:])

  def get_counts(self, state):
    key = self.get_key(state)
    if key not in self.state_counts_map:
      return {}
    return self.state_counts_map[key]

  def add_point(self, state, next, weight=1):
    state = state[-self.order:]
    if len(state) == self.order:

      key = self.get_key(state)
      if key not in self.state_counts_map:
        self.state_counts_map[key] = {}

      counts = self.state_counts_map[key]
      if next not in counts:
        counts[next] = 0
      counts[next] += 1

class MarkovFeeder:
  def __init__(self, markov):
    self.markov = markov
    self.history = []

  def dump_data(self):
    return self.markov.dump_data()

  def add_point(self, input, output):
    self.history.append(input)
    self.markov.add_point(self.history, output)

  def get_counts(self, state):
    return self.markov.get_counts(state)


class RPSPatternMatcher:
  def __init__(self, order):
    self.order = order
    self.combined_response = MarkovFeeder(TieredMarkov(order, 9))
    self.victory_response = MarkovFeeder(TieredMarkov(order, 27))
    self.opponent_response = MarkovFeeder(TieredMarkov(order, 27))
    self.history = []

  def dump_data(self):
    return [ self.combined_response.dump_data()
           , self.victory_response.dump_data()
           , self.opponent_response.dump_data()]

  def register_throw(self,our_throw, opponent_throw):
    if self.history:
      weight = 2 ** (len(self.history) / 30)
      our_last, opponent_last = self.history[-1]
      self.combined_response.add_point(our_last * 3 + opponent_last, opponent_throw)
      self.victory_response.add_point((opponent_last - our_last + 3) % 3, (opponent_throw - opponent_last + 3) % 3)
      self.opponent_response.add_point(opponent_last, opponent_throw)

    self.history.append((our_throw, opponent_throw))

  def next_throw(self):
    our_last, opponent_last = self.history[-1]
    victory_offset_counts = self.victory_response.get_counts([(b - a + 3) % 3 for a,b in self.history[-self.order:]])
    victory_counts = dict(((opponent_last + key + 3) % 3, val) for key, val in victory_offset_counts.items())
    combined_counts = dict((key, val*3) for key, val in 
      self.combined_response.get_counts([a*3 + b for a,b in self.history[-self.order:]]).items())
    opponent_counts = self.opponent_response.get_counts([b for a,b in self.history[-self.order:]])
    d_list = [ victory_counts
             , combined_counts
             , opponent_counts
             ]
    total_counts = [sum(d.get(i,0) for d in d_list) for i in range(3)]

    if sum(total_counts) == 0:
      return random.choice(range(3))

    total_scores = [max([total_counts[(i+2)%3] - total_counts[(i+1)%3],0]) for i in range(3)]
    total_scores = [i + 0.5 * sum(total_scores) for i in total_scores]
    rand = random.random() * sum(total_scores)
    for i, weight in enumerate(total_scores):
      rand -= weight
      if rand <= 0:
        return i

if input == "":
  venom = [int(i) for i in "01100212010200122211210112000202202111102220121221001112200221211010120201102100010000220112220011012212002110010221110111121210212202000121120222221020210220000112111201201110002101002010110201220100120010102112211022101210022212222022120212120012102011210010122001002211012121220120001111101001100122112011011010202002000002122210111021211220220102202021200002012111120020211021110020022002100211212020011202112001110120101011221022221210102221112222201112110001202211222120122202101200222000222200"]
  types = ["R", "P", "S"]
  matcher = RPSPatternMatcher(4)
  last_throw = venom[0]
  round_num = 0
  lost = 0

if input in types:
  matcher.register_throw(last_throw, types.index(input))
  if (last_throw - types.index(input) + 3) % 3 == 2:
    lost += 1

  if round_num < 500 and lost < (round_num / 3) + 10:
    last_throw = venom[round_num]
  else:
    last_throw = matcher.next_throw()
else:
  last_throw = random.choice(range(3))

round_num += 1
output = types[last_throw]