Skip to content

Instantly share code, notes, and snippets.

@mat3u
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mat3u/068b79ea838d0f8d83b0 to your computer and use it in GitHub Desktop.
Save mat3u/068b79ea838d0f8d83b0 to your computer and use it in GitHub Desktop.
Engine for TronAI game - bots playing TRON
type Direction = | North | East | South | West
type Position = int * int
type Size = int * int
type Player = int
type World = { Taken : Position list; Heads : (Player * Position) list }
type Bot = Player -> Position -> World -> Direction
let skipLast list =
// TODO: Ultra inefficient
list |> List.rev |> List.tail |> List.rev
let moveHead (x,y) direction =
match direction with
| North -> (x, y-1)
| East -> (x+1, y)
| South -> (x, y+1)
| West -> (x-1, y)
let randomPosition (r: System.Random) ((w,h): Size) =
(r.Next(w), r.Next(h))
let initializeGame (r: System.Random) ((w,h): Size) (players: Player seq) =
let heads = List.ofSeq (seq {
for p in players do
yield (p, randomPosition r (w,h))
})
let border = List.ofSeq ((seq {
for x in [-1; w] do
for y in -1 .. h do
yield (x,y)
for x in -1..w do
for y in [-1;h] do
yield (x,y)
yield! heads |> Seq.map snd
}) |> Seq.distinct)
{Taken = border; Heads = heads}
let turn (bots: (Player * Bot) seq) (world: World) : World =
let participants = query {
for bot in bots do
join player in world.Heads
on (fst(bot) = fst(player))
select (fst(bot), snd(bot), snd(player))
}
let potentialMoves = participants
|> Seq.toList
|> List.map (fun (player, bot, position_n) -> (player, bot player position_n world, position_n))
|> List.map (fun (player, direction, position_n) -> (player, moveHead position_n direction))
let potentialTakens = potentialMoves
|> List.scan (fun taken (_, position) -> position :: taken) world.Taken
|> skipLast
let survivors = List.zip potentialMoves potentialTakens
|> List.choose (fun ((player, position), taken) ->
match (taken |> List.exists (fun c -> c = position)) with
| false -> Some (player, position)
| _ -> None
) |> Seq.toList
let taken = world.Taken |> Seq.append (survivors |> Seq.map snd) |> Seq.toList
{Taken = taken; Heads = survivors}
let game (r: System.Random) (size: Size) (bots: (Player * Bot) seq) =
let initialWorld = initializeGame r size (bots |> Seq.map fst)
let turn' = turn (bots |> Seq.toList)
initialWorld
|> Seq.unfold (fun world ->
match world.Heads |> Seq.length with
| 0 -> None
| 1 -> None
| _ ->
let next = turn' world
Some (next, next)
)
open System
let render (w, h) worlds =
let colors = [ConsoleColor.Red; ConsoleColor.Blue; ConsoleColor.Cyan; ConsoleColor.Yellow; ConsoleColor.Green; ConsoleColor.Magenta]
let renderTurn (w, h) world =
world.Heads |> Seq.iter (fun (player, (x, y)) ->
let org = Console.ForegroundColor
Console.ForegroundColor <- colors.[player % colors.Length]
Console.SetCursorPosition(x+1, y+1)
Console.Write(player)
Console.ForegroundColor <- org
System.Threading.Thread.Sleep(10)
)
let initial::history = worlds
initial.Taken |> Seq.iter (fun (x, y) ->
Console.SetCursorPosition(x+1, y+1)
Console.Write("#")
)
worlds |> Seq.iter (renderTurn (w,h))
let kingOfTheNorth (_ : Player) (_: Position) (_: World) =
North
let wriggler (me : Player) ((x,y): Position) (world: World) =
let isEmpty potential =
world.Taken |> Seq.exists (fun c -> c = potential) |> not
if isEmpty (x, y-1) then North
else if isEmpty (x-1, y) then West
else if isEmpty (x+1, y) then East
else South
let wrigglerL (_ : Player) ((x,y): Position) (world: World) =
let isEmpty potential =
world.Taken |> Seq.exists (fun c -> c = potential) |> not
if isEmpty (x, y+1) then South
else if isEmpty (x+1, y) then East
else if isEmpty (x-1, y) then West
else North
let loony (_ : Player) ((x,y): Position) (world: World) =
let r = new System.Random(x * y)
let isEmpty potential =
world.Taken |> Seq.exists (fun c -> c = potential) |> not
let options = seq {
yield ((x, y+1), South)
yield ((x, y-1), North)
yield ((x+1, y), East)
yield ((x-1, y), West)
}
let valid = options
|> Seq.choose (fun (p, d) ->
match isEmpty p with
| false -> None
| _ -> Some d
) |> Seq.toList
match valid.Length with
| 0 -> North
| n -> valid.[r.Next(n)]
[<EntryPoint>]
let main argv =
let random = new System.Random()
let size = (70, 35)
let bots = [wriggler; wrigglerL; loony; loony; kingOfTheNorth; wriggler]
|> List.mapi (fun i b -> (i, b))
let history = game random size bots |> Seq.toList
history |> render size
let survivors = (history |> Seq.last).Heads
match survivors.Length with
| 0 -> Console.Write(" DRAW ")
| 1 -> Console.Write("<- WINNER ")
Console.ReadKey()
0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment