Advertisement
SciQuest_csp

MCTS-AI, ConnectFour

Mar 23rd, 2012
176
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 5.92 KB | None | 0 0
  1. ConnectFour = class()
  2.  
  3. -- ConnectFour was the first game class i implemented, and has a few quirks due to that.
  4. -- the prefix on these params is for historical purposes and can be safely removed.
  5. -- "chain" represents the number in a row to win, and turns the game into Connect3, 4, 5, etc.
  6.  function ConnectFour:setupParams()
  7.      iparameter("ConnectColumns", 1, 7)
  8.      iparameter("ConnectRows", 1, 7)
  9.      iparameter("ConnectChain", 1, 7)
  10.  
  11.      ConnectColumns = readProjectData("connectCol", 4)
  12.      ConnectRows = readProjectData("connectRows", 4)
  13.      ConnectChain = readProjectData("connectChain", 3)
  14.  end
  15.  
  16.  function ConnectFour:saveParams()
  17.      saveProjectData("connectCol", ConnectColumns)
  18.      saveProjectData("connectRows", ConnectRows)
  19.      saveProjectData("connectChain", ConnectChain)
  20.  end
  21.  
  22.  function ConnectFour:init()
  23.      self.empty = -1
  24.      self.player = 0
  25.      self.ai = 1
  26.      
  27.      self.gameWon = nil
  28.  
  29.     -- this is to remind myself that i intend to have an 'undo' feature.    
  30.     -- self.moveHistory = {}
  31.      
  32.      self.turn = AIstarts
  33.      self.numMoves = ConnectColumns
  34.      
  35.      self.columns = {}
  36.      self.numberOfRows = ConnectRows
  37.      self.numberOfColumns = ConnectColumns
  38.      self.goal = ConnectChain
  39.      
  40.      for i = 1, self.numberOfColumns do
  41.          local temp = {}
  42.          for j = 1, self.numberOfRows do
  43.              table.insert(temp, j, self.empty)
  44.          end
  45.          table.insert(self.columns, i, temp)
  46.      end
  47.  end
  48.  
  49.  function ConnectFour:copy(original)
  50.      for i = 1, self.numberOfColumns do
  51.          for j = 1, self.numberOfRows do
  52.              self.columns[i][j] = original.columns[i][j]
  53.          end
  54.      end
  55.      
  56.      self.numMoves = original.numMoves
  57.      self.turn = original.turn
  58.      self.gameWon = original.gameWon
  59.  end
  60.  
  61.  function ConnectFour:draw()
  62.      local dx = WIDTH/(self.numberOfColumns)
  63.      local dy = HEIGHT/(self.numberOfRows)
  64.      ellipseMode(CORNER)
  65.      
  66.      for i,col in pairs(self.columns) do
  67.          pushMatrix()
  68.          for j,disc in pairs(col) do
  69.              if disc == self.player then
  70.                  fill(red)
  71.              elseif disc == self.ai then
  72.                  fill(blue)
  73.              else
  74.                  fill(gray)
  75.              end
  76.              ellipse(0,0,math.min(dx, dy))
  77.              translate(0, dy)
  78.          end
  79.          popMatrix()
  80.          translate(dx, 0)
  81.      end
  82.  end
  83.  
  84. -- only invalid if the column is filled.
  85.  function ConnectFour:validMove(col)
  86.      if self.columns[col][self.numberOfRows] == self.empty then
  87.          return true
  88.      else
  89.          return false
  90.      end
  91.  end
  92.  
  93. -- just a Get function
  94.  function ConnectFour:numberOfMoves()
  95.      return self.numMoves
  96.  end
  97.  
  98. -- do not pass this func 0. add a check if it becomes problematic.
  99. -- (when i was first testing, this function was the performance bottleneck
  100. -- and performance was terrible, so i omitted the check!)
  101.  function ConnectFour:randomMove(numberOfPossibleMoves)
  102.      local myMove = math.random(numberOfPossibleMoves)
  103.      for i = 1, self.numberOfColumns do
  104.          if self.columns[i][self.numberOfRows] == self.empty then
  105.              myMove = myMove - 1
  106.              if myMove == 0 then
  107.                  self:move(i)
  108.                  return
  109.              end
  110.          end
  111.      end
  112.  end
  113.  
  114. -- could just call flipTurn(self) instead of this.
  115. -- actually, im not sure if this is called at all... delete it?
  116.  function ConnectFour:changeTurn()
  117.      if self.turn == self.player then self.turn = self.ai
  118.      else self.turn = self.player end
  119.  end
  120.  
  121.  function ConnectFour:move(selection)
  122.      local done = false
  123.      for i,disc in pairs(self.columns[selection]) do
  124.          if done == false and disc == self.empty then
  125.              self.columns[selection][i] = self.turn
  126.              self:updateWinner(selection, i)
  127.              done = true
  128.              if i == self.numberOfRows then
  129.                  self.numMoves = self.numMoves - 1
  130.              end
  131.          end
  132.      end
  133.      flipTurn(self)
  134.  end
  135.  
  136. -- no protection against trying to make an invalid move. fix this.
  137.  function ConnectFour:touched(t)
  138.      local selection = t.x/(WIDTH/self.numberOfColumns)
  139.      selection = math.ceil(selection)
  140.      self:move(selection)
  141.  end
  142.  
  143. -- near optimal. only checks the minimal number of spots on the board  
  144.  function ConnectFour:updateWinner(x, y)
  145.      if self.gameWon ~= nil then return end
  146.  
  147.      local winner = nil
  148.      local goal = self.goal - 1
  149.    
  150.      for i = 1, 4 do
  151.          self:resetChain()
  152.          for j = -goal, goal do
  153.              if i==1 then winner = self:countChain(x+j, y)
  154.              elseif i==2 then winner = self:countChain(x, y+j)
  155.              elseif i==3 then winner = self:countChain(x+j, y+j)
  156.              elseif i==4 then winner = self:countChain(x+j, y-j) end
  157.              if winner ~= nil then
  158.                  self.gameWon = winner
  159.                  return
  160.              end
  161.          end
  162.      end
  163.  end
  164.  
  165. -- this and the following function work to count pieces in a row, to detect wins.
  166.  function ConnectFour:countChain(i, j)
  167.      if i < 1 or self.numberOfColumns < i then return nil end
  168.      if j < 1 or self.numberOfRows < j then return nil end
  169.      if self.type ~= self.columns[i][j] then
  170.          self.type = self.columns[i][j]
  171.          self.count = 0
  172.      end
  173.      if self.type ~= self.empty and self.type == self.columns[i][j] then
  174.          self.count = self.count + 1
  175.          if self.count == self.goal then
  176.              return self.type
  177.          end
  178.      end
  179.      return nil
  180.  end
  181.  
  182.  function ConnectFour:resetChain()
  183.      self.count = 0
  184.      self.type = self.empty
  185.  end
  186.  
  187.  function ConnectFour:gameOver()
  188.      return self.gameWon
  189.  end
  190.  
  191.  function ConnectFour:possibleMovesInterval()
  192.      return vec2(1,self.numberOfColumns)
  193.  end
  194.  
  195. -- no efficient heuristic to my knowledge, so we just call it a draw.
  196.  function ConnectFour:positionStrength()
  197.      return 0.5
  198.  end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement