7 people like it.
Like the snippet!
Game of 2048
Simple implementation of the popular game "2048". Can you add up the tiles and reach 2048?
The game can be played in fsi or you can add the GUI which is so far missing.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
|
/// This is a simple implementation of the 2048 game
/// It's playable in fsi, I might hook it up to an app later using Xamarin's tooling.
/// But so far it was just funny to implement the rules.
namespace T2048
module Game =
type Row = List<int>
type Board = List<Row>
type Direction = LEFT | RIGHT | UP | DOWN
let NEW_TILES = [2; 4]
let mutable theScore = 0
let addScore s = theScore <- theScore + s
// Here is the main logic of the game, moving the tiles and combining identical neighbours
// We always move from right to left, other directions are performed by turning the board first
let rec moveRow r =
match r with
| [] -> []
| 0::bs -> moveRow bs
| [a] -> [a]
| a::b::bs when b=0 -> moveRow(a::bs)
| a::b::bs when a=b -> addScore(a+b) ; (a+b)::moveRow bs
| a::b::bs -> a::moveRow(b::bs)
let pad elem toLen bs =
let padLen = toLen - List.length bs
if padLen<=0 then bs else
let newTail = [for i in 1 .. padLen -> elem]
bs @ newTail
let moveBoard b =
let size = List.length b
let moveAndPad = moveRow >> (pad 0 size)
List.map moveAndPad b
let reverseBoard = List.map List.rev
let rec transposeBoard b =
match b with
| [] -> []
| []::_ -> []
| _ ->
let heads = List.map List.head b
let tails = List.map List.tail b
heads :: transposeBoard tails
// Turn the board, move left, and turn back
let moveDirection d =
match d with
| LEFT -> moveBoard
| RIGHT -> reverseBoard >> moveBoard >> reverseBoard
| UP -> transposeBoard >> moveBoard >> transposeBoard
| DOWN -> transposeBoard >> reverseBoard >> moveBoard >> transposeBoard >> List.rev
let emptySlots b =
let slot i j v = ((i,j),v)
let numberRow f row = List.mapi f row
let numberBoard = List.mapi (fun i -> numberRow (slot i))
let empties = List.choose (fun (a,b) -> if b=0 then Some(a) else None)
b |> numberBoard |> List.concat |> empties
let rnd = new System.Random()
let oneOf vs =
let i = rnd.Next(List.length vs)
List.nth vs i
let updateBoard coords v board =
let (posRow,posCol) = coords
let updateRow pos v row = List.mapi (fun i b -> if i=pos then v else b) row
List.mapi (fun i row -> if i=posRow then updateRow posCol v row else row) board
let addNewTile b =
let empties = emptySlots b
let slotCoords = oneOf empties
let v = oneOf NEW_TILES
updateBoard slotCoords v b
// To detect "game over", try a direction which is 90 degress from the current one and see if any tiles can move
let otherDirection d =
match d with
| LEFT -> UP
| RIGHT -> UP
| UP -> LEFT
| DOWN -> LEFT
// Perform a move
let nextMove b d =
let newBoard = b |> (moveDirection d) |> addNewTile
let gameOver = newBoard=b && (moveDirection (otherDirection d) b)=b
(newBoard, gameOver)
// Install a pretty printer if we are running in fsi
let buildString sep l = List.foldBack (fun a acc -> sprintf "%4i %s %s" a sep acc) l ""
let printBoard (b:Board) =
b |> List.map (buildString "|") |> List.reduce (fun a b -> a+"\n"+b)
#if INTERACTIVE
fsi.AddPrinter printBoard
#endif
// Here is the current state
let mutable theBoard:Board = []
// Start a new game
let reset () =
let _ = theScore <- 0
let _ = theBoard <- [[0;0;0;0];
[0;0;0;0];
[0;0;0;0];
[0;0;0;0]] |> addNewTile
theBoard
// Make a move and update the current state
let move d =
let (newBoard, gameOver) = nextMove theBoard d
let _ = if gameOver then printf "Game over, score=%i" theScore
let _ = theBoard <- newBoard
theBoard
|
type Row = List<int>
Full name: T2048.Game.Row
Multiple items
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
type Board = List<Row>
Full name: T2048.Game.Board
type Direction =
| LEFT
| RIGHT
| UP
| DOWN
Full name: T2048.Game.Direction
union case Direction.LEFT: Direction
union case Direction.RIGHT: Direction
union case Direction.UP: Direction
union case Direction.DOWN: Direction
val NEW_TILES : int list
Full name: T2048.Game.NEW_TILES
val mutable theScore : int
Full name: T2048.Game.theScore
val addScore : s:int -> unit
Full name: T2048.Game.addScore
val s : int
val moveRow : r:int list -> int list
Full name: T2048.Game.moveRow
val r : int list
val bs : int list
val a : int
val b : int
val pad : elem:'a -> toLen:int -> bs:'a list -> 'a list
Full name: T2048.Game.pad
val elem : 'a
val toLen : int
val bs : 'a list
val padLen : int
val length : list:'T list -> int
Full name: Microsoft.FSharp.Collections.List.length
val newTail : 'a list
val i : int
val moveBoard : b:int list list -> int list list
Full name: T2048.Game.moveBoard
val b : int list list
val size : int
val moveAndPad : (int list -> int list)
val map : mapping:('T -> 'U) -> list:'T list -> 'U list
Full name: Microsoft.FSharp.Collections.List.map
val reverseBoard : (int list list -> int list list)
Full name: T2048.Game.reverseBoard
val rev : list:'T list -> 'T list
Full name: Microsoft.FSharp.Collections.List.rev
val transposeBoard : b:'a list list -> 'a list list
Full name: T2048.Game.transposeBoard
val b : 'a list list
val heads : 'a list
val head : list:'T list -> 'T
Full name: Microsoft.FSharp.Collections.List.head
val tails : 'a list list
val tail : list:'T list -> 'T list
Full name: Microsoft.FSharp.Collections.List.tail
val moveDirection : d:Direction -> (int list list -> int list list)
Full name: T2048.Game.moveDirection
val d : Direction
val emptySlots : b:int list list -> (int * int) list
Full name: T2048.Game.emptySlots
val slot : ('a -> 'b -> 'c -> ('a * 'b) * 'c)
val i : 'a
val j : 'b
val v : 'c
val numberRow : ((int -> 'a -> 'b) -> 'a list -> 'b list)
val f : (int -> 'a -> 'b)
val row : 'a list
val mapi : mapping:(int -> 'T -> 'U) -> list:'T list -> 'U list
Full name: Microsoft.FSharp.Collections.List.mapi
val numberBoard : (int list list -> ((int * int) * int) list list)
val empties : (((int * int) * int) list -> (int * int) list)
val choose : chooser:('T -> 'U option) -> list:'T list -> 'U list
Full name: Microsoft.FSharp.Collections.List.choose
val a : int * int
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val concat : lists:seq<'T list> -> 'T list
Full name: Microsoft.FSharp.Collections.List.concat
val rnd : System.Random
Full name: T2048.Game.rnd
namespace System
Multiple items
type Random =
new : unit -> Random + 1 overload
member Next : unit -> int + 2 overloads
member NextBytes : buffer:byte[] -> unit
member NextDouble : unit -> float
Full name: System.Random
--------------------
System.Random() : unit
System.Random(Seed: int) : unit
val oneOf : vs:'a list -> 'a
Full name: T2048.Game.oneOf
val vs : 'a list
System.Random.Next() : int
System.Random.Next(maxValue: int) : int
System.Random.Next(minValue: int, maxValue: int) : int
val nth : list:'T list -> index:int -> 'T
Full name: Microsoft.FSharp.Collections.List.nth
val updateBoard : int * int -> v:'a -> board:'a list list -> 'a list list
Full name: T2048.Game.updateBoard
val coords : int * int
val v : 'a
val board : 'a list list
val posRow : int
val posCol : int
val updateRow : (int -> 'b -> 'b list -> 'b list)
val pos : int
val v : 'b
val row : 'b list
val b : 'b
val addNewTile : b:int list list -> int list list
Full name: T2048.Game.addNewTile
val empties : (int * int) list
val slotCoords : int * int
val v : int
val otherDirection : d:Direction -> Direction
Full name: T2048.Game.otherDirection
val nextMove : b:int list list -> d:Direction -> int list list * bool
Full name: T2048.Game.nextMove
val newBoard : int list list
val gameOver : bool
val buildString : sep:string -> l:int list -> string
Full name: T2048.Game.buildString
val sep : string
val l : int list
val foldBack : folder:('T -> 'State -> 'State) -> list:'T list -> state:'State -> 'State
Full name: Microsoft.FSharp.Collections.List.foldBack
val acc : string
val sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val printBoard : b:Board -> string
Full name: T2048.Game.printBoard
val b : Board
val reduce : reduction:('T -> 'T -> 'T) -> list:'T list -> 'T
Full name: Microsoft.FSharp.Collections.List.reduce
val a : string
val b : string
val mutable theBoard : Board
Full name: T2048.Game.theBoard
val reset : unit -> Board
Full name: T2048.Game.reset
val move : d:Direction -> Board
Full name: T2048.Game.move
val printf : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printf
More information