dotsandboxes/players.rkt

122 lines
4.3 KiB
Racket
Raw Permalink Normal View History

#lang racket
; dots and boxes players!
(require "main.rkt" racket/random)
2022-04-10 19:21:11 +00:00
(provide player-random player-first player-last player-greedy player-generous player-nico player-horizontal player-vertical)
; a Player that plays random moves
(define player-random
(player
"Random"
(lambda (g n)
(let ([m (car (random-sample (valid-moves g) 1))])
(line (line-from m) (line-to m) n)))))
; plays the first valid move found
(define player-first
(player
"First Move"
(lambda (g n)
(let ([m (first (valid-moves g))])
(line (line-from m) (line-to m) n)))))
; plays the last valid move found
(define player-last
(player
"Last Move"
(lambda (g n)
(let ([m (last (valid-moves g))])
(line (line-from m) (line-to m) n)))))
; if a move completes a box, play it. Otherwise, play a random move.
; This isn't a "perfect" greedy - it will sometimes take a single box instead of two.
(define player-greedy
(player
"Greedy"
(lambda (g n)
(let* ([moves (valid-moves g)]
[box-finishing-moves
(filter (lambda (m) (not (= 0 (completes-boxes g m)))) moves)])
(cond
[(empty? box-finishing-moves)
(let ([m (car (random-sample (valid-moves g) 1))])
(line (line-from m) (line-to m) n))]
[else (line (line-from (first box-finishing-moves)) (line-to (first box-finishing-moves)) n)])))))
; helper function for determining if a move makes a three-sided box.
(define (three? gr l)
(let
([boxes (boxes-for l gr)]
[g (append-move gr l)])
(not (= 0 (length (filter (lambda (b) (= (count-box b g) 3)) boxes))))))
; if a move completes a box, play it. Otherwise, play a move that avoids making a three-sided box. Otherwise, play a random move.
; this is reflective of a common simple human strategy, and the strategy that the program author uses. Hence, it is named for them.
(define player-nico
(player
"Nico-bot"
(lambda (g n)
(let* ([moves (valid-moves g)]
[non-three-moves (filter (lambda (m) (not (three? g m))) moves)]
[box-finishing-moves
(filter (lambda (m) (not (= 0 (completes-boxes g m)))) moves)])
(cond
[(not (empty? box-finishing-moves)) (line (line-from (first box-finishing-moves)) (line-to (first box-finishing-moves)) n)]
[(not (empty? non-three-moves))
(let ([m (car (random-sample non-three-moves 1))])
(line (line-from m) (line-to m) n))]
[else
(let ([m (car (random-sample (valid-moves g) 1))])
(line (line-from m) (line-to m) n))])))))
; tries to give up boxes to the opponent whenever possible and deliberately avoids taking boxes ever
(define player-generous
(player
"Generous"
(lambda (g n)
(let* ([moves (valid-moves g)]
[three-moves (filter (lambda (m) (three? g m)) moves)]
[non-box-finishing-moves
(filter (lambda (m) (= 0 (completes-boxes g m))) moves)])
(cond
[(not (empty? three-moves))
(let ([m (car (random-sample three-moves 1))])
(line (line-from m) (line-to m) n))]
[(not (empty? non-box-finishing-moves))
(let ([m (car (random-sample non-box-finishing-moves 1))])
(line (line-from m) (line-to m) n))]
2022-04-10 19:21:11 +00:00
[else
(let ([m (car (random-sample (valid-moves g) 1))])
(line (line-from m) (line-to m) n))])))))
; prefers vertical moves.
(define player-vertical
(player
"Vertical"
(lambda (g n)
(let* ([moves (valid-moves g)]
[vertical-moves (filter (curry vertical?) moves)])
(cond
[(not (empty? vertical-moves))
(let ([m (car (random-sample vertical-moves 1))])
(line (line-from m) (line-to m) n))]
[else
(let ([m (car (random-sample (valid-moves g) 1))])
(line (line-from m) (line-to m) n))])))))
; prefers horizontal moves.
(define player-horizontal
(player
"Horizontal"
(lambda (g n)
(let* ([moves (valid-moves g)]
[horizontal-moves (filter (curry horizontal?) moves)])
(cond
[(not (empty? horizontal-moves))
(let ([m (car (random-sample horizontal-moves 1))])
(line (line-from m) (line-to m) n))]
[else
(let ([m (car (random-sample (valid-moves g) 1))])
(line (line-from m) (line-to m) n))])))))