########### Rock Paper Sissors Game Model in PythonACTR ######################
from ccm.env.objects import * #http://www.carleton.ca/ics/ccmlab/ccmsuite.html
from ccm.lib.actr import *    #http://www.carleton.ca/ics/ccmlab/ccmsuite.html
import sys

########### Define the environment ###########################################
class RPS_PGC:
  goal=Buffer()
  io=GenericModule()
  procedural=Procedural()
  PMNoise(procedural,noise=0.01)
  PMpgc(procedural)

  def play_r(goal='play rps'):  
    io.choice='rock'
    goal.clear()
  def play_p(goal='play rps'):  
    io.choice='paper'
    goal.clear()
  def play_s(goal='play rps'):  
    io.choice='scissors'
    goal.clear()

########## Define the model ##################################################
class RPS_Lag2:
  goal=Buffer()
  retrieve=Buffer()
  io=GenericModule()
  procedural=Procedural()
  memory=Memory(retrieve,threshold=None)  # to make sure chunks are retrieved
  memNoise=DMNoise(memory,noise=0.3)  #noise variation for the chunks
  memBase=DMBaseLevel(memory,decay=0.5)  #rate of decay of chunks in memory
  memSpread=DMSpreading(memory,goal)
  memSpread.strength=0  #spreading activation from the goal buffer
  
  def init(): # initialize with a memory of the game rules
    memory.add('paper beats rock')
    memory.add('rock beats scissors')
    memory.add('scissors beats paper')
   
  def play(goal='play rps ?x ?y'): # production
    memory.request('pattern ?x ?y ?z')
    goal.set('predict')

  def predict(goal='predict',retrieve='pattern ?x ?y ?z'): # production
    memory.request('? beats ?z')
    goal.set('respond')

  def predictNothing(goal='predict',memory='error:True'): # production
    memory.request('? beats ?')
    goal.set('respond')

  def respond(goal='respond',retrieve='?x beats ?y'): # production
    io.choice=x     
    goal.clear()
    
  def remember(goal='remember ?x ?y ?z'): # production
    memory.add('pattern ?x ?y ?z')
    goal.clear()


# a simple model which gets user input instead of doing anything itself
class HumanPlayer:
  goal=Buffer()
  io=GenericModule()
  procedural=Procedural()

  def play(goal='play rps'):
   while 1:
    c=raw_input('Choose (R)ock, (P)aper, or (S)cissors:')
    if len(c)>0:
      c=c[0].lower() # convert to lower case for matching
      if c in 'rps':
        if c=='r': io.choice='rock'
        elif c=='p': io.choice='paper'
        elif c=='s': io.choice='scissors'
        goal.clear()
        break


def runGame():
  history1=[None,None]
  history2=[None,None]
  score1=0
  score2=0
  for i in range(150): # max of 150 trials 
    player1.goal.set('play rps %s %s'%(history2[-2],history2[-1]))
    player1.run()
    player2.goal.set('play rps %s %s'%(history1[-2],history1[-1]))
    player2.run()
    
    c1=player1.io.choice
    history1.append(c1)
    
    c2=player2.io.choice
    history2.append(c2)
    
    player1.goal.set('remember %s %s %s'%(history2[-3],history2[-2],history2[-1]))
    player1.run()
    player2.goal.set('remember %s %s %s'%(history1[-3],history1[-2],history1[-1]))
    player2.run()
  
    if c1==c2: result='TIE' # scoring is outside of the model per se
    elif (c1,c2) in (('paper','rock'),('rock','scissors'),('scissors','paper')):
      score1+=1
      result='PLAYER 1 WINS'
      player1.procedural.success()
      player2.procedural.failure()
    else:  
      score2+=1
      result='PLAYER 2 WINS'
      player2.procedural.success()
      player1.procedural.failure()
      
    print '%10s(%3d)%10s(%3d)%24s'%(c1,score1,c2,score2,result) # after 150 trials

########## run model(s) of game ##################################################
player1=ACTR(RPS_Lag2)
player1.logging=False   # turn off messages
player1=ACTR(RPS_PGC)
#player2=ACTR(RPS_Lag2)
#player2.memNoise.noise=0.3
#player2.memSpread.strength=0
player2=ACTR(HumanPlayer)
player2.logging=False   # turn off messages
runGame() # this command starts the game

