reset password
Author Message
M_Hsu
Posts: 19
Posted 16:48 Oct 11, 2013 |

Reply to the topic and sign up for a problem.

Lecture Slides:

http://www.stanford.edu/class/cs97si/05-combinatorial-games.pdf

Problems for the topic:

http://www.stanford.edu/class/cs97si/assn5.html

rabbott
Posts: 1649
Posted 21:45 Oct 12, 2013 |

After reading the Stanford slides and this slower-paced discussion of these sorts of games, it's not clear to me what computational tricks one should learn from this. If there is a pattern in the problem, it's not likely that one could write a program to find it without first solving the problem. In addition, all these problems are candidates for dynamic programming solutions.  Here's a dynamic programming solution to "1143 Number Game," which was listed as one of the 3 hardest on the Stanford list.

object NumberGame {

  // There are no winning moves from the empty set
  private var archive = Map[TreeSet[Int], TreeSet[Int]](TreeSet[Int]() -> TreeSet())

  /**
   * compute and return the excluded closure of excluded,
   * i.e., all the numbers that are excluded (up to max)
   * given that the numbers in excluded are excluded.
   *
   * This is not done efficiently, and no archive is created.
   */
  private def excludedClosure(excluded: Set[Int], max: Int): Set[Int] = {
    val additionalExcluded =
      for (
        i <- excluded;
        j <- excluded;
        val sum = i + j;
        if (sum <= max && !excluded.contains(sum))
      ) yield sum
    if (additionalExcluded.isEmpty) excluded
    else excludedClosure(excluded ++ additionalExcluded, max)
  }

  private def prunedNumbers(numbers: TreeSet[Int]): TreeSet[Int] = {
    if (numbers.isEmpty) TreeSet()
    else {
      val max = numbers.max
      val excluded = (2 to max).toSet.diff(numbers)
      numbers -- excludedClosure(excluded, max)
    }
  }

  private def sort(set: Set[Int]): TreeSet[Int] = TreeSet[Int]() ++ set

  /**
   * Given a set of numbers return the winning moves. Format the winning moves with
   *        move -> remaining numbers
   * on a separate line for each winning move.
   *
   * If the input is not valid, elements are removed to make it valid.
   */
  def winningMoves(numbers: Set[Int]): String = {
    val sortedNumbers = sort(numbers)

    val validNumbers = prunedNumbers(sortedNumbers)
    if (!validNumbers.equals(sortedNumbers)) {
      println("Validating: " + sortedNumbers + " - " + (sortedNumbers -- validNumbers) + " ==> " + validNumbers)
      winningMoves(validNumbers)
    } else {

      // This is the only important line. It returns a possibly empty List of winning moves.
      val winners = winningMoves1(sortedNumbers)

      // The rest formats the output. For each winning move, show what's left.
      val remainders = winners.map(move => prunedNumbers(sortedNumbers - move))
      "\n" + (winners.zip(remainders)).map(mr => mr._1 + " -> [" + mr._2.mkString(", ") + ']').mkString("\n")
    }
  }

  /**
   * Given a set of numbers, return a set of winning moves.
   */
  private def winningMoves1(numbers: TreeSet[Int]): TreeSet[Int] =
    archive.getOrElse(
      numbers,
      {
        val max = numbers.max
        val excluded = TreeSet[Int]() ++ ((2 to max).toSet.diff(numbers))
        val sortedWinners =
          sort(
            for (
              n <- numbers;
              val remainingNumbers = prunedNumbers(numbers - n);
              if (winningMoves1(remainingNumbers)).isEmpty
            ) yield n)
        archive += numbers -> sortedWinners
        sortedWinners
      })
}

Here are some sample runs from a worksheet

import NumberGame._
object numberGameWS {
 
  winningMoves(Set(2, 3, 4, 6, 7, 9, 11, 14, 16, 17))

                                         //> Validating: TreeSet(2, 3, 4, 6, 7, 9, 11, 14, 16, 17) - TreeSet(16, 17) ==> 
                                         //| TreeSet(2, 3, 4, 6, 7, 9, 11, 14)
                                         //| res0: String = "
                                         //| 7 -> [2, 3, 4, 6, 9, 11]"
  winningMoves(Set(2, 3, 4, 6, 7, 9, 11, 14))    
                                         //> res1: String = "
                                         //| 7 -> [2, 3, 4, 6, 9, 11]"

  winningMoves(Set(2, 3, 4, 6, 9, 11))          
                                         //> res2: String = "
                                         //| "
  winningMoves(Set(2, 3, 4, 6, 9, 10, 11))        

                                         //> Validating: TreeSet(2, 3, 4, 6, 9, 10, 11) - TreeSet(10) ==> TreeSet(2, 3, 4
                                         //| , 6, 9, 11)
                                         //| res3: String = "
                                         //| "

  winningMoves(Set(2, 3, 4, 5, 7, 8, 9, 10))      
                                         //> res4: String = "
                                         //| 5 -> [2, 3, 4, 7, 8, 9]"
  winningMoves(Set(2, 3, 4, 5, 7, 8, 9))          
                                         //> res5: String = "
                                         //| 5 -> [2, 3, 4, 7, 8, 9]
                                         //| 7 -> [2, 3, 4, 5, 8, 9]
                                         //| 8 -> [2, 3, 4, 5, 7, 9]"
  winningMoves(Set(2, 3, 4, 7, 8, 9))            
                                         //> res6: String = "
                                         //| "
  winningMoves(Set(2, 3, 4, 5, 7, 8))            
                                         //> res7: String = "
                                         //| 4 -> [2, 3, 5, 7]"
  winningMoves(Set(2, 3, 4, 5, 7))                
                                         //> res8: String = "
                                         //| 4 -> [2, 3, 5, 7]
                                         //| 5 -> [2, 3, 4, 7]
                                         //| 7 -> [2, 3, 4, 5]"
  winningMoves(Set(2, 3, 5, 6, 7, 9, 11))        
                                         //> res9: String = "
                                         //| 6 -> [2, 3, 5, 7, 9, 11]"
  winningMoves(Set(2, 3, 5, 6))                  
                                         //> res10: String = "
                                         //| "
  winningMoves(Set(2, 3, 6, 7))                  

                                          //> res11: String = "
                                          //| "
 
  winningMoves(Set(2, 3, 5, 7, 12, 16, 20))  

                                         //> Validating: TreeSet(2, 3, 5, 7, 12, 16, 20) - TreeSet(12, 16, 20) ==> TreeSe
                                         //| t(2, 3, 5, 7)
                                         //| res12: String = "
                                         //| "

  winningMoves(Set(2, 3, 5))                      
                                         //> res13: String = "
                                         //| 5 -> [2, 3]"
  winningMoves(Set(2, 3))                        

                                         //> res14: String = "
                                         //| "
  winningMoves(Set(2, 3, 8))                      

                                         //> Validating: TreeSet(2, 3, 8) - TreeSet(8) ==> TreeSet(2, 3)
                                         //| res15: String = "
                                         //| "
  winningMoves(Set(2, 3, 4, 5, 6, 7))            

                                         //> res16: String = "
                                         //| "
  winningMoves(Set(2, 3, 4, 5, 6, 8))            

                                         //> res17: String = "
                                         //| 4 -> [2, 3, 5, 6]"

  winningMoves(Set(2))                            
                                         //> res18: String = "
                                         //| 2 -> []"
  winningMoves(Set(3))                            

                                         //> res19: String = "
                                         //| 3 -> []"

  winningMoves(Set(3, 4))                        
                                         //> Validating: TreeSet(3, 4) - TreeSet(4) ==> TreeSet(3)
                                         //| res20: String = "
                                         //| 3 -> []"

}

 

Last edited by rabbott at 20:13 Oct 13, 2013.
rabbott
Posts: 1649
Posted 20:14 Oct 13, 2013 |

I've modified the numbers game code so that it validates the input before looking for a move.

M_Hsu
Posts: 19
Posted 07:35 Oct 18, 2013 |

Here is a good intro to the subject:

 

http://www.math.ucla.edu/~tom/Game_Theory/comb.pdf

lbriggs
Posts: 57
Posted 07:52 Oct 18, 2013 |

New Stone-Forfex-Cloth Game