rps-giant

This program has been disqualified.


Authorblue-tomato
Submission date2015-07-08 07:50:49.846426
Rating8292
Matches played207
Win rate82.13

Source code:

# author: blue-tomato (Matthew B. Smith)

# uses large pieces of code from:
# 		fltbot (author: Flight Odyssey), which in turn is a variation of dllu1 by dllu (modified constants)
#		rps_cracker (author: Rekrul)

# Flight Odyssey suggests in the comments for fltbot: see also www.dllu.net/rps

# I chose to use these two submissions, because fltbot is based on rfind, while rps_cracker is based on 
# the frequencies of sequences of various lengths. These two types of strategies perform fairly randomly
# against each other, so I hope that a bot that uses both should be more versatile than either. It is possible
# a future version of this program will contain predictive chunks from other rps bots.

# my unique contribution is in my style of meta-prediction. rather than producing 3 rotations of each strategy,
# and using a frequency predictor to select between them, I track a "meta-history" of how many rotations each 
# selector has been off of the correct answer (the correct answer being the one that beats the input). I then
# use rfind on this meta-history to decide which rotation is ideal. Of course, I need to use a frequency-style
# predictor to decide between the various resulting meta-histories based on the different predictors.

# my chief concern is that rps-giant will exceed the 5 second match limit. If this happens I will have to submit
# a stream-lined version of this program.

# I have neglected to have a random fallback for this program. I may be conceited, but I currently believe this
# program is good enough compared to the current competition that a fallback is not currently needed.

# I have tested this program against the current top 5 on the leaderboard (fltbot by FlightOdyssey, NOOB MUHAMMED by 
# PRO YUNUS, nervous scattershot by rspk, rps_cracker by Rekrul, and RVL V3 by Thatguy), and rps-giant consistently
# wins over 50 percent of matches against each.

import random

if input == '':
	
	### setup - things for general use or used by meta predictors
	
	beat = {'R':'P','P':'S','S':'R','X':'X'}
	pnum = {'RR':'0','RP':'1','RS':'2','PR':'3','PP':'4','PS':'5','SR':'6','SP':'7','SS':'8'}
	mpnum = {'AA':'0','AB':'1','AC':'2','BA':'3','BB':'4','BC':'5','CA':'6','CB':'7','CC':'8','XA':'a','XB':'b','XC':'c','AX':'d','BX':'e','CX':'f','XX':'x'}
	mpnumi = {'0':'A','1':'A','2':'A','3':'B','4':'B','5':'B','6':'C','7':'C','8':'C','a':'X','b':'X','c':'X','d':'A','e':'B','f':'C','x':'X'}
	mpnumo = {'0':'A','1':'B','2':'C','3':'A','4':'B','5':'C','6':'A','7':'B','8':'C','a':'A','b':'B','c':'C','d':'X','e':'X','f':'X','x':'X'}
	mdec = {'A':{'R':'R','P':'P','S':'S','X':'X'},'B':{'R':'P','P':'S','S':'R','X':'X'},'C':{'R':'S','P':'R','S':'P','X':'X'}}
	
	### setup for history
	
	length = 0
	
	hin = ''
	hout = ''
	hpair = ''
	
	hinlengths = [8,15]
	hinidx = [0,2]
	hinsize = 2
	
	houtlengths = [8,15]
	houtidx = [4,6]
	houtsize = 2
	
	hpairlengths = [3,8,15]
	hpairidx = [8,10,12]
	hpairsize = 3
	
	### setup for Rekrul's rps_cracker code (I could do this myself but man am I lazy)
	SIZE = 4
	WEIGHT_FACTOR = 7.
	class HistoryNode(object):
		def __init__(self, parent=None):
			if parent is not None:
				self.depth = parent.depth + 1
			else:
				self.depth = 0

			self.children = {'RR': None, 'RS': None, 'RP': None, 'SR': None, 'SS': None,
							 'SP': None, 'PR': None, 'PS': None, 'PP': None}

			self.distribution = {'RR': 0, 'RS': 0, 'RP': 0, 'SR': 0, 'SS': 0, 'SP': 0,
								 'PR': 0, 'PS': 0, 'PP': 0}

		def new_move(self, input):
			last_move = input[0:2]

			if len(input) > 2:
				if self.children[last_move] is None:
					self.children[last_move] = HistoryNode(self)
				self.children[last_move].new_move(input[2:])
			else:
				self.distribution[last_move] = self.distribution[last_move] * 0.975 + 1

		def predict(self, input):
			if len(input) > 0:
				last_move = input[0:2]
				if self.children[last_move] is not None:
					return self.children[last_move].predict(input[2:])
				else:
					return None
			else:
				return self.distribution


	class HistoryTree(object):
		def __init__(self):
			self.root = HistoryNode()

			self.input = ''

		def new_move(self, move):
			self.input += move
			if len(self.input) > SIZE * 2:
				self.input = self.input[-SIZE * 2:]

			for i in xrange(2, len(self.input) + 1, 2):
				self.root.new_move(self.input[-i:])

		def predict(self):
			results = {'R':0, 'S':0, 'P':0}
			for i in xrange(2, len(self.input) + 1, 2):
				res = self.root.predict(self.input[-i:])
				if res is not None:
					for key in res:
						results[key[1]] += res[key] * (WEIGHT_FACTOR ** i)

			d = results
			e = d.keys()
			e.sort(cmp=lambda a, b: cmp(d[a], d[b]))
			return e[-1]
		
		def predict_i(self, i):
			results = {'R':0, 'S':0, 'P':0}
			for depth in xrange(i, -1, -1):
				res = self.root.predict(self.input[-depth * 2:])
				if res is not None:
					break
			
			if res is None:
				return 'X'
			else:
				for key in res:
					results[key[1]] += res[key]
				
			d = results
			e = d.keys()
			e.sort(cmp=lambda a, b: cmp(d[a], d[b]))
			return e[-1]

	history_tree_me = HistoryTree()
	history_tree_him = HistoryTree()
			
	### setup for fltbot/dllu1 strange-predictor 
	
	centrifuge = {'RP':0,'PS':1,'SR':2,'PR':3,'SP':4,'RS':5,'RR':6,'PP':7,'SS':8}
	centripete={'R':0,'P':1,'S':2}
	
	flt_soma = [0.0]*9;
	flt_rps = [1.0,1.0,1.0];
	
	flt_revsoma = [0.0]*9;
	flt_revrps = [1.0,1.0,1.0]
	
	flt_a = "RPS"
	
	### additional general setup
		
	numpred = 28 # number of predictors (for convenience)
	
	mh = ['']*numpred # meta-histories
	
	choice = ['']*numpred # stores choice for output of basic predictors 
	mchoice = ['']*numpred # stores cchoice for meta-predictors that use meta-rfind technique
	mscore = [0.0]*numpred # scores meta predictors
	
	output = 'X'
	
else:

	### create meta-history and score meta-predictors
	for i in range(0,numpred):
	
		# create meta history
		mhoninput = 'X'
		if choice[i] == beat[input]:
			mhoninput = 'A'
		elif choice[i] == input:
			mhoninput = 'B'
		elif choice[i] == beat[beat[input]]:
			mhoninput = 'C'
			
		mhonoutput = 'X'
		if choice[i] == beat[output]:
			mhonoutput = 'A'
		elif choice[i] == output:
			mhonoutput = 'B'
		elif choice[i] == beat[beat[output]]:
			mhonoutput = 'C'
			
		mh[i] += mpnum[mhoninput+mhonoutput]
		
		# score meta-predictors
		# using scoring from rps_cracker
		if mchoice[i] == beat[input]:
			mscore[i] += 1.0
		elif mchoice[i] == beat[beat[input]]:
			mscore[i] -= 1.0
		elif mchoice[i] == input:
			mscore[i] -= 0.34 
		elif mchoice[i] == 'X':
			mscore[i] -= 0.11333
		mscore[i] *= 0.90

	length += 1
	hin += input 
	hout += output 
	hpair += pnum[input+output]
	
	#########################################################################
	### basic predictors ####################################################
	#########################################################################
	
	### predict based on input history
	bhinloc = -1
	hinloc = [-1]*hinsize
	hinref = 0
	for i in range(1,min(16,length)):
		loc = hin[:-1].rfind(hin[-i:])
		if loc != -1:
			bhinloc = loc+i
			if i == hinlengths[hinref]:
				hinloc[hinref] = bhinloc 
				hinref += 1
		else:
			break
	while hinref < hinsize:
		hinloc[hinref] = bhinloc 
		hinref += 1
	if bhinloc == -1:
		for i in range(hinidx[0],hinidx[hinsize-1]+2):
			choice[i] = 'X'
	else:
		for i in range(0,hinsize):
			j = hinidx[i]
			choice[j+0] = beat[hin[hinloc[i]]]
			choice[j+1] = beat[beat[hout[hinloc[i]]]]
	
	### predict based on output history 
	bhoutloc = -1
	houtloc = [-1]*houtsize 
	houtref = 0
	for i in range(1,min(16,length)):
		loc = hout[:-1].rfind(hout[-i:])
		if loc != -1:
			bhoutloc = loc+i
			if i == houtlengths[houtref]:
				houtloc[houtref] = bhoutloc 
				houtref += 1
		else:
			break
	while houtref < houtsize:
		houtloc[houtref] = bhoutloc 
		houtref += 1
	if bhoutloc == -1:
		for i in range(houtidx[0],houtidx[houtsize-1]+2):
			choice[i] = 'X'
	else:
		for i in range(0,houtsize):
			j = houtidx[i]
			choice[j+0] = beat[hin[houtloc[i]]]
			choice[j+1] = beat[beat[hout[houtloc[i]]]]
	
	### predict based on pairwise history
	bhpairloc = -1
	hpairloc = [-1]*hpairsize 
	hpairref = 0
	for i in range(1,min(16,length)):
		loc = hpair[:-1].rfind(hpair[-i:])
		if loc != -1:
			bhpairloc = loc+i 
			if i == hpairlengths[hpairref]:
				hpairloc[hpairref] = bhpairloc 
				hpairref += 1
		else:
			break
	while hpairref < hpairsize:
		hpairloc[hpairref] = bhpairloc
		hpairref += 1 
	if bhpairloc == -1:
		for i in range(hpairidx[0],hpairidx[hpairsize-1]+2):
			choice[i] = 'X'
	else:
		for i in range(0,hpairsize):
			j = hpairidx[i]
			choice[j+0] = beat[hin[hpairloc[i]]]
			choice[j+1] = beat[beat[hout[hpairloc[i]]]]
			
	### predict based on pairwise history with delay
	### predictor taken from fltbot/dllu1
	hpairdelayloc = -1
	for i in range(2,min(9,length)):
		loc = hpair[:-2].rfind(hpair[-i:-1])
		if loc != -1:
			hpairdelayloc = loc+i 
		else:
			break
	if hpairdelayloc == -1:
		choice[14] = 'X'
		choice[15] = 'X'
	else:
		choice[14] = beat[hin[hpairdelayloc]]
		choice[15] = beat[beat[hout[hpairdelayloc]]]
		
	### frequency of move-sequences predictor taken from rps_cracker
	
	history_tree_me.new_move(output + input)
	history_tree_him.new_move(input + output)

	choice[16] = history_tree_me.predict()
	choice[17] = history_tree_him.predict()
	
	for j in xrange(SIZE):
		choice[18+j*2] = history_tree_me.predict_i(j)
		choice[19+j*2] = history_tree_him.predict_i(j)

	### strange predictor taken from fltbot/dllu1. It appears many people have copied
	### this predictor, so if I use it better it should help outperform them
	
	flt_soma[centrifuge[input+output]] += 1.0;
	flt_rps[centripete[input]] += 1.0;
	
	flt_best = [0.0,0.0,0.0]
	flt_best[0] = flt_soma[centrifuge[output+'R']]*flt_rps[0]
	flt_best[1] = flt_soma[centrifuge[output+'P']]*flt_rps[1]
	flt_best[2] = flt_soma[centrifuge[output+'S']]*flt_rps[2]
	choice[26] = flt_a[flt_best.index(max(flt_best))]
	
	flt_revsoma[centrifuge[output+input]] += 1.0
	flt_revrps[centripete[output]] += 1.0 
	
	flt_revbest = [0.0,0.0,0.0]
	flt_revbest[0] = flt_revsoma[centrifuge[input+'R']]*flt_revrps[0]
	flt_revbest[1] = flt_revsoma[centrifuge[input+'P']]*flt_revrps[1]
	flt_revbest[2] = flt_revsoma[centrifuge[input+'S']]*flt_revrps[2]
	choice[27] = flt_a[flt_revbest.index(max(flt_revbest))]

	
	#############################################################################
	### meta predictors (using rfind) ###########################################
	#############################################################################
	
	for j in range(0,numpred):
		mhloc = -1
		for i in range(1,min(16,length)):
			loc = mh[j][:-1].rfind(mh[j][-i:])
			if loc != -1:
				mhloc = loc+i
			else:
				break
	
		mpred = mh[j][mhloc]
		if mhloc == -1 or mpnumi[mpred] == 'X':
			mchoice[j] = 'X'
		else:
			mchoice[j] = mdec[mpnumi[mpred]][choice[j]]
	
	output = mchoice[mscore.index(max(mscore))]

if output == 'X':
	output = random.choice('RPS')