Generating game moves functionally with Scala

I'm trying to figure out how to write strategy games using Scala functionally, but unfortunately I seem to be stuck at the very basics. (This is not homework, but my attempts to learn something new, namely "pure" functional programming.)

Take the following simple β€œgame”: the player (the only one) has x identical pieces on an infinite row of squares. Pieces start at square 0, and each move he can move one piece forward one square.

I will use as a data structure List[Int], each element is the position (square) of one part.

To generate the possible steps, I came up with:

def moves(start: List[Int]) = 
    (0 until start.length).map({i => start.updated(i, start(i) + 1)});

val m1 = moves(List(0,0,0))
// m1 then contains Vector(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1))

val m2 = moves(List(1,2,3))
// m1 then contains Vector(List(2, 2, 3), List(1, 3, 3), List(1, 2, 4))

What I don't like is the use of the index loop (0 until start.length). This does not seem to me "functional." Is this the right way to do this, or is there a better way?


Now, in my example game, all the parts are identical, so in the case of m1all three possible movements are also identical and can / should be combined in one move. I changed movesto sort each move item so that I can get a list of individual items:

def moves(start: List[Int]) = 
    (0 until start.length).map({i => start.updated(i, start(i) + 1).sorted}).distinct;

val m1 = moves(List(0,0,0))
// m1 then contains Vector(List(0, 0, 1))

val m2 = moves(List(1,2,3))
// m1 then contains Vector(List(2, 2, 3), List(1, 3, 3), List(1, 2, 4))

However, this requires that the data structure be sortable, and in my "real" application it most likely is not List[Int], but a Tuple or case class. I think I need a method distinctthat takes a function that defines equality. How to implement this?

+5
2

, , . [Int, Int], , , ( , ).

def moves(start: Map[Int,Int]) = start.keySet.map(k => {
  val n = start(k)
  val pickup = (if (n == 1) (start - k) else start + (k -> (n-1)))
  pickup + ((k+1) -> (start.getOrElse(k+1, 0) + 1))
})

(, , ). :

scala> val firstmoves = moves(Map(0->3))                          
firstmoves: scala.collection.Set[scala.collection.immutable.Map[Int,Int]] =
Set(Map((0,2), (1,1)))

scala> val secondmoves = firstmoves.flatMap(moves)                           
secondmoves: scala.collection.Set[scala.collection.immutable.Map[Int,Int]] =
Set(Map((0,1), (1,2)), Map((0,2), (2,1)))

scala> val thirdmoves = secondmoves.flatMap(moves)
thirdmoves: scala.collection.Set[scala.collection.immutable.Map[Int,Int]] =
Set(Map((1,3)), Map((0,1), (1,1), (2,1)), Map((0,2), (3,1)))
+4

(0 until start.length) start.indices. :

def moves(start: List[Int]): List[List[Int]] =  start match {
  case Nil => Nil
  case head :: tail => (head + 1 :: tail) :: (moves(tail) map (head :: _))
}

, , , , . , .

. , , . :

head + 1 :: tail

, . , , solutions , :

solutions map (solution => head :: solution)

, ,

solutions map (head :: _)

solutions. , , : moves ! tail :

(moves(tail) map (head :: _))

, , , .

, , .

, , equals, , , distinct, .

, SortedSet - Ordering - . :

object LO extends Ordering[List[Int]] {
  def compare(x: List[Int], y: List[Int]) = cmp(x.sorted, y.sorted)
  def cmp(x: List[Int], y: List[Int]): Int = (x, y) match {
    case (Nil, Nil) => 0
    case (Nil, _  ) => -1
    case (_  , Nil) => 1
    case (h1 :: t1, h2 :: t2) if h1 < h2 => -1
    case (h1 :: t1, h2 :: t2) if h2 < h1 => 1
    case (h1 :: t1, h2 :: t2) => cmp(t1, t2)
  }
}

val m1 = SortedSet(moves(List(0, 0, 0)): _*)(LO).toList
+4

All Articles