/ botc.factor
botc.factor
  1  ! Copyright (C) 2024 Aleksander "olus2000" Sabak.
  2  ! See https://factorcode.org/license.txt for BSD license.
  3  USING: accessors arrays db db.sqlite db.tuples db.types
  4  io.backend io.directories kernel literals math math.functions
  5  math.parser namespaces sets sequences sequences.extras splitting
  6  strings ;
  7  IN: esolang_games.botc
  8  
  9  
 10  ! Model
 11  
 12  PREDICATE: alignment < string { "good" "evil" } in? ;
 13  
 14  
 15  TUPLE: player player-id elo ;
 16  
 17  : <player> ( player-id elo -- player ) player boa ;
 18  
 19  
 20  TUPLE: player-game
 21    player-id
 22    game-id
 23    { alignment maybe{ alignment } } ;
 24  
 25  : <player-game> ( player-id game-id alignment -- player-game )
 26    player-game boa ;
 27  
 28  
 29  TUPLE: game game-id { winning-side maybe{ alignment } } ;
 30  
 31  : <game> ( game-id winning-side -- game ) game boa ;
 32  
 33  
 34  ! Tables
 35  
 36  CONSTANT: botc-db-path
 37    $[ "vocab:esolang_games/botc/db.sqlite" normalize-path ]
 38  
 39  
 40  player "player"
 41  { { "player-id" "playerid" VARCHAR +user-assigned-id+ }
 42    { "elo" "elo" INTEGER } } define-persistent
 43  
 44  
 45  player-game "playergame"
 46  { { "player-id" "playerid" VARCHAR +user-assigned-id+
 47      { +foreign-id+ player "playerid" } }
 48    { "game-id" "gameid" INTEGER +user-assigned-id+
 49      { +foreign-id+ game "gameid" } }
 50    { "alignment" "alignment" VARCHAR } } define-persistent
 51  
 52  
 53  game "game"
 54  { { "game-id" "gameid" INTEGER +user-assigned-id+ }
 55    { "winning-side" "winningside" VARCHAR } } define-persistent
 56  
 57  
 58  CONSTANT: botc-tables { player game player-game }
 59  
 60  
 61  : with-botc-db ( ..a quot: ( ..a -- ..b ) -- ..b )
 62    botc-db-path <sqlite-db> swap with-db ; inline
 63  
 64  
 65  : ensure-botc-db ( -- )
 66    botc-db-path touch-file
 67    [ botc-tables ensure-tables ] with-botc-db ;
 68  
 69  
 70  : recreate-botc-db ( -- )
 71    [ botc-tables [ recreate-table ] each ] with-botc-db ;
 72  
 73  
 74  ! Quick inserting game
 75  
 76  ! Syntax:
 77  ! <game-nr> <winning-alignment>
 78  ! <player-name>;<player-alignment>
 79  ! <player-name>;<player-alignment>
 80  ! ...
 81  
 82  SYMBOL: current-game
 83  
 84  
 85  : parse-player ( player-string -- player-game )
 86    ";" split first2 current-game get swap <player-game> ;
 87  
 88  
 89  : parse-game ( game-string -- {player-game} game )
 90    split-lines unclip " " split first2
 91    [ string>number dup ] dip <game>
 92    [ current-game [ [ parse-player ] map ] with-variable ] dip ;
 93  
 94  
 95  : insert-game ( game-string -- )
 96    parse-game [ insert-tuple [ insert-tuple ] each ]
 97    with-botc-db ;
 98  
 99  
100  ! Stats
101  
102  : data-table ( {selectors} -- table )
103    [ T{ player } select-tuples swap
104      '[ _ [ call( player -- datum ) ] with map ] map ]
105    with-botc-db ;
106  
107  
108  : pretty-table ( {selectors} colnum -- ) 2drop ; ! TODO
109  
110  
111  : player-games ( player -- player-games )
112    f f <player-game> select-tuples ;
113  
114  
115  : player-alignment-games ( player alignment -- player-games )
116    f swap <player-game> select-tuples ;
117  
118  
119  : player-games>wins ( player-games -- games )
120    [ [ game-id>> ] [ alignment>> ] bi <game> select-tuples ]
121    map-concat ;
122  
123  
124  : win-rate ( player -- win-rate )
125    player-id>> player-games [ player-games>wins ] keep
126    [ length ] bi@ / ;
127    
128  
129  : score ( player -- score )
130    player-id>> player-games [ player-games>wins ] keep
131    [ length 1 + ] bi@ 1 + / ;
132  
133  ! Elo calculation
134  
135  
136  CONSTANT: starting-elo 1500
137  
138  
139  : add-player ( name -- )
140    starting-elo <player> [ insert-tuple ] with-botc-db ;
141  
142  
143  : game-id>player-games ( game-id -- {player-game} )
144    f swap f <player-game> select-tuples ;
145  
146  
147  : player-game>player ( player-game -- player )
148    player-id>> f <player> select-tuple ;
149  
150  
151  : game-id>game ( game-id -- game ) f <game> select-tuple ;
152  
153  
154  : predict-pairing ( good-elo evil-elo -- winrate )
155    - 10 400 / swap ^ 1 + 1 swap / ;
156  
157  
158  : predict-game ( good-players evil-players -- winrate )
159      [ [ elo>> ] map ] bi@
160      [ predict-pairing ] cartesian-map concat
161      [ sum ] [ length ] bi / ;
162  
163  
164  : update-player-elo ( player win-rate -- )
165    32 * round >integer '[ _ + ] change-elo update-tuple ;
166  
167  
168  : update-elo ( game-id -- )
169    [ [ game-id>player-games [ alignment>> "good" = ] partition
170      [ [ player-game>player ] map ] bi@ 2dup predict-game ] keep
171      game-id>game winning-side>> "evil" = [ 1 - ] when tuck -1 *
172      [ '[ _ update-player-elo ] each ] 2bi@ ] with-botc-db ;
173