IocainePowder[v1]

AuthorEmmanuel Harish Menon
Submission date2019-05-31 21:10:18.249648
Rating4561
Matches played229
Win rate46.72

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

Source code:

'''
Program Name: IocainePowder_SubmissionCode[v1].py
Written By: Emmanuel Harish Menon
Last Updated: 7:09 AM on 1/6/19

Explanation:
Iocaine Powder was originally conceived by Dan Egnor for the First International RoShamBo Programming Competition. This is program is merely my interpretation of the explanation he provided on his website. 
'''
#import modules
import random
import operator

#output    
output = ""

#counts number of plays
playCounter = {"R": 0, "P": 0, "S": 0}
#counts wins for each predictive algorithm
winTypeCounter = {"random": 0, "freq2": 0, "freq5": 0, "freq10": 0, "freq100": 0, "freq1000": 0, "history2": 0, "history5": 0, "history10": 0, "history100": 0, "history1000": 0}

#stores meta strategy names, makes it easier to iterate through stat dictionary
metaStratList = ["naive", "secondGuess", "thirdGuess", "oppNaive", "oppSecondGuess", "oppThirdGuess"]

#stores stats for each predictive algorithm's meta strategy (I'm almost certain there is a better way to do this)
randomStats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}

freqStats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}
freq2Stats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}
freq5Stats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}
freq10Stats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}
freq100Stats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}
freq1000Stats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}

history2Stats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}
history5Stats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}
history10Stats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}
history100Stats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}
history1000Stats = {"naive": 0, "secondGuess": 0, "thirdGuess": 0, "oppNaive": 0, "oppSecondGuess": 0, "oppThirdGuess": 0}

#list of moves to play to win
winningMoves = {"R": "P", "P": "S", "S": "R"}
#list of choices
choices = ["R", "P", "S"]


#stores opponents move history
oppMoveHistory = []
#stores bot's move history
myMoveHistory = []

#counts number of plays
numPlays = 1

#returns the key of the largest value in dictionary
def returnDictMax(dictionary):
    return max(dictionary.items(), key=operator.itemgetter(1))[0]

#metastrategy, origin is used to determine which of the dictionaries the program should use to pick its strategy
def metaStrategy(oppPick, origin = ""):
    #metastrategy
    def naive():
        return winningMoves[oppPick]
    def secondGuess():
        return winningMoves[winningMoves[naive()]]
    def thirdGuess():
        return winningMoves[winningMoves[secondGuess()]]
    def oppNaive():
        return winningMoves[naive()]
    def oppSecondGuess():
        return winningMoves[secondGuess()]
    def oppThirdGuess():
        return winningMoves[thirdGuess()]

    #returns a winning value
    def decisionMaker(dictMax):
        if dictMax == "naive":
            return naive()
        elif dictMax == "secondGuess":
            return secondGuess()
        elif dictMax == "thirdGuess":
            return thirdGuess()
        elif dictMax == "oppNaive":
            return oppNaive()
        elif dictMax == "oppSecondGuess":
            return oppSecondGuess()
        elif dictMax == "oppThirdGuess":
            return oppThirdGuess()

    #this just simplifies the code below
    def picker(dictMax):
        if dictMax != 0:
            pick = decisionMaker(dictMax)
        else:
            pick = choices[random.randint(0, 2)]
        return pick
    
    #if the function is being run from the randomGuesser() function    
    if origin == "random":
        dictMax = returnDictMax(randomStats)
        pick = picker(dictMax)
        return pick

    #if the function is being run from the freqAnalysis(2) function        
    elif origin == "freq2":
        dictMax = returnDictMax(freq2Stats)
        pick = picker(dictMax)
        return pick
    
    #if the function is being run from the freqAnalysis(5) function        
    elif origin == "freq5":
        dictMax = returnDictMax(freq5Stats)
        pick = picker(dictMax)
        return pick

    #if the function is being run from the freqAnalysis(10) function        
    elif origin == "freq10":
        dictMax = returnDictMax(freq10Stats)
        pick = picker(dictMax)
        return pick

    #if the function is being run from the freqAnalysis(100) function        
    elif origin == "freq100":
        dictMax = returnDictMax(freq100Stats)
        pick = picker(dictMax)
        return pick

    #if the function is being run from the freqAnalysis(1000) function        
    elif origin == "freq1000":
        dictMax = returnDictMax(freq1000Stats)
        pick = picker(dictMax)
        return pick

        #if the function is being run from the historyMatcher(2) function        
    elif origin == "history2":
        dictMax = returnDictMax(history2Stats)
        pick = picker(dictMax)
        return pick
    
    #if the function is being run from the historyMatcher(5) function        
    elif origin == "history5":
        dictMax = returnDictMax(history5Stats)
        pick = picker(dictMax)
        return pick

    #if the function is being run from the historyMatcher(10) function        
    elif origin == "history10":
        dictMax = returnDictMax(history10Stats)
        pick = picker(dictMax)
        return pick

    #if the function is being run from the historyMatcher(100) function        
    elif origin == "history100":
        dictMax = returnDictMax(history100Stats)
        pick = picker(dictMax)
        return pick

    #if the function is being run from the historyMatcher(1000) function        
    elif origin == "history1000":
        dictMax = returnDictMax(history1000Stats)
        pick = picker(dictMax)
        return pick

    #this is the origin value when we are updating the stats in the table
    elif origin == "test":
        return [winChecker(naive(), oppMoveHistory[-1]), winChecker(secondGuess(), oppMoveHistory[-1]), winChecker(thirdGuess(), oppMoveHistory[-1]), winChecker(oppNaive(), oppMoveHistory[-1]), winChecker(oppSecondGuess(), oppMoveHistory[-1]), winChecker(oppThirdGuess(), oppMoveHistory[-1])]

#the returnVal var is used so that the function can determine whether or not to return the predicted opponent's pick or the move that beats it
def randomGuesser(returnVal = "winning move"):
    oppPick = choices[random.randint(0,2)]
    if returnVal == "winning move":
        return metaStrategy(oppPick, "random")
    elif returnVal == "predicted move":
        return oppPick

#does frequency analysis within a specified range
def freqAnalysis(searchRange, returnVal = "winning move"):
    #stores number of each play within range
    dictPlaysInRange = {"R": 0, "P": 0, "S": 0}

    #stores a separate list to oppMoveHistory
    tempList = oppMoveHistory[-searchRange:]

    #updates dict
    for pick in tempList:
        dictPlaysInRange[pick] += 1

    #returns max
    oppPick = returnDictMax(dictPlaysInRange)
    
    if returnVal == "winning move":
        return metaStrategy(oppPick, "freq"+str(searchRange))
    elif returnVal == "predicted move":
        return oppPick

#searchRange is the range that the function will search in and checkSequence is a number which tells the function how many moves it wants to compare to
def historyMatcher(checkSequence, searchRange = len(oppMoveHistory), returnVal = "winning move"):
    #list that needs to be found
    checkList = oppMoveHistory[-checkSequence:]
    searchList = oppMoveHistory[-searchRange:-checkSequence]
    arrLength = len(searchList)
    oppPick = ""

    #runs as long as the array is longer than the list that needs to be found
    while (arrLength >= checkSequence):
        check = searchList[-checkSequence-1:-1]
        #if it is found, break out of the while loop
        if check == checkList:
            oppPick = searchList[-1]
            break
        #remove last element from list
        searchList.pop()
        #reset arrLength
        arrLength = len(searchList)
    
    
    #if nothing has been found, random (still debating whether to include or not)
    if oppPick == "":
        oppPick = choices[random.randint(0, 2)]
    
    if returnVal == "winning move":
        return metaStrategy(oppPick, "history"+str(checkSequence))
    elif returnVal == "predicted move":
        return oppPick

#updates relevant dict stats
def metaStratUpdater(winList, dictionaryName):
    for index, item in enumerate(winList):
        if item == "W":
            dictionaryName[metaStratList[index]] += 1
        elif item == "L":
                dictionaryName[metaStratList[index]] -= 1

#used to check for wins, losses and draws
def winChecker(output, _input):
    if output == _input:
        return "D"
    elif winningMoves[_input] == output:
        return "W"
    elif winningMoves[output] == _input:
        return "L"

stratFunctionList = [randomGuesser, freqAnalysis, historyMatcher]

#if the previous input is not empty
if input is not "":
    #add the opponents previous move to the list and increment the relevant value in the dictionary
    oppMoveHistory.append(input)

    lenOfList = len(oppMoveHistory)

    #updates win/lose/draw results for each
    for function in stratFunctionList:
        if function is randomGuesser:
            #the move var is just the predicted move
            move = function()
            winList = metaStrategy(function("predicted move"), origin = "test")
            #check to see if that move would have won
            moveResult = winChecker(move, input)
            if moveResult == "W":
                winTypeCounter["random"] += 1
                metaStratUpdater(winList, randomStats)
            elif moveResult == "L":
                winTypeCounter["random"] -= 1
                metaStratUpdater(winList, randomStats)

        elif function is freqAnalysis:
            if lenOfList > 2:
                move = function(2)
                moveResult = winChecker(move, input)
                winList = metaStrategy(function(2, returnVal = "predicted move"), origin = "test")
                if moveResult == "W":
                    winTypeCounter["freq2"] += 1
                elif moveResult == "L":
                    winTypeCounter["freq2"] -= 1
                    
                metaStratUpdater(winList, freq2Stats)
                
            if lenOfList > 5:
                move = function(5)
                moveResult = winChecker(move, input)
                winList = metaStrategy(function(5, returnVal = "predicted move"), origin = "test")
                
                if moveResult == "W":
                    winTypeCounter["freq5"] += 1
                elif moveResult == "L":
                    winTypeCounter["freq5"] -= 1
                    
                metaStratUpdater(winList, freq5Stats)
            
            if lenOfList > 10:
                move = function(10)
                moveResult = winChecker(move, input)
                winList = metaStrategy(function(10, returnVal = "predicted move"), origin = "test")

                if moveResult == "W":
                    winTypeCounter["freq10"] += 1
                elif moveResult == "L":
                    winTypeCounter["freq10"] -= 1
                    
                metaStratUpdater(winList, freq10Stats)

            if lenOfList > 100:
                move = function(100)
                moveResult = winChecker(move, input)
                winList = metaStrategy(function(100, returnVal = "predicted move"), origin = "test")
                
                if moveResult == "W":
                    winTypeCounter["freq100"] += 1
                elif moveResult == "L":
                    winTypeCounter["freq100"] -= 1
                    
                metaStratUpdater(winList, freq100Stats)

            if lenOfList > 1000:
                move = function(1000)
                moveResult = winChecker(move, input)
                winList = metaStrategy(function(1000, returnVal = "predicted move"), origin = "test")

                if moveResult == "W":
                    winTypeCounter["freq1000"] += 1
                elif moveResult == "L":
                    winTypeCounter["freq1000"] -= 1
                
                metaStratUpdater(winList, freq1000Stats)
                
        elif function is historyMatcher:
            if lenOfList > 4:
                move = function(2)
                moveResult = winChecker(move, input)
                winList = metaStrategy(function(2, lenOfList, returnVal = "predicted move"), origin = "test")
                if moveResult == "W":
                    winTypeCounter["history2"] += 1
                elif moveResult == "L":
                    winTypeCounter["history2"] -= 1
                    
                metaStratUpdater(winList, history2Stats)
                
            if lenOfList > 10:
                move = function(5)
                moveResult = winChecker(move, input)
                winList = metaStrategy(function(5, lenOfList, returnVal = "predicted move"), origin = "test")
                
                if moveResult == "W":
                    winTypeCounter["history5"] += 1
                elif moveResult == "L":
                    winTypeCounter["history5"] -= 1
                    
                metaStratUpdater(winList, history5Stats)
            
            if lenOfList > 20:
                move = function(10)
                moveResult = winChecker(move, input)
                winList = metaStrategy(function(10, lenOfList, returnVal = "predicted move"), origin = "test")

                if moveResult == "W":
                    winTypeCounter["history10"] += 1
                elif moveResult == "L":
                    winTypeCounter["history10"] -= 1
                    
                metaStratUpdater(winList, history10Stats)

            if lenOfList > 200:
                move = function(100)
                moveResult = winChecker(move, input)
                winList = metaStrategy(function(100, lenOfList, returnVal = "predicted move"), origin = "test")
                
                if moveResult == "W":
                    winTypeCounter["history100"] += 1
                elif moveResult == "L":
                    winTypeCounter["history100"] -= 1
                    
                metaStratUpdater(winList, history100Stats)

            if lenOfList > 2000:
                move = function(1000)
                moveResult = winChecker(move, input)
                winList = metaStrategy(function(1000, lenOfList, returnVal = "predicted move"), origin = "test")

                if moveResult == "W":
                    winTypeCounter["history1000"] += 1
                elif moveResult == "L":
                    winTypeCounter["history1000"] -= 1
                
                metaStratUpdater(winList, history1000Stats)

    playCounter[input] += 1

#first move is random
if len(oppMoveHistory) == 0:
    output = choices[random.randint(0, 2)]
else:
    #the strategy that has the highest score is picked, if there is no "highest score" then random
    dictMax = returnDictMax(winTypeCounter)
    
    if winTypeCounter[dictMax] <= 0 or dictMax == "random":
        output = randomGuesser()
    elif dictMax == "freq2":
        output = freqAnalysis(2)
    elif dictMax == "freq5":
        output = freqAnalysis(5)
    elif dictMax == "freq10":
        output = freqAnalysis(10)
    elif dictMax == "freq100":
        output = freqAnalysis(100)
    elif dictMax == "freq1000":
        output = freqAnalysis(1000)
    elif dictMax == "history2":
        output = historyMatcher(2)
    elif dictMax == "history5":
        output = historyMatcher(5)
    elif dictMax == "history10":
        output = historyMatcher(10)
    elif dictMax == "history100":
        output = historyMatcher(100)
    elif dictMax == "history1000":
        output = historyMatcher(1000)

#not currently doing anything with this variable but...    
myMoveHistory.append(output)

numPlays += 1