-
Notifications
You must be signed in to change notification settings - Fork 1
/
remote-admin.rkt
182 lines (154 loc) · 6.88 KB
/
remote-admin.rkt
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#lang racket
;; ---------------------------------------------------------------------------------------------------
;; remote proxy administrator and remote proxy turn
;; -- implement interface of administrator and player turn, respectively
;; -- communicates with remote proxy player on server side to implement admin/turn functionality
(require "admin-intf.rkt")
(admin& remote-administrator% remote-turn% DONE EXHAUSTED SCORE)
;; ---------------------------------------------------------------------------------------------------
;; IMPLEMENTATION
(require (only-in "admin.rkt" DONE EXHAUSTED SCORE) "remote-actor.rkt" "state.rkt" "Lib/io.rkt")
(module+ test
(require rackunit
"admin.rkt" "state.rkt" (only-in "player.rkt" create) "strategy.rkt"
(submod "state.rkt" sample-states) (submod "board.rkt" tiles+spots)))
;; for the interaction diagram, see protocols.rkt -- the below are remote
;; proxies that simulate full-fledged administrators and turns on the
;; client computer so that the player does not need to change
(define remote-administrator%
(class/remote
(init-field (next-tile void))
(field (*player #f))
;; signup the client player and obtain unique name
(define/public (sign-up local-name player)
(set! *player player)
(handle (sign up)
(xsend (signup-writer local-name))
(set! name (signup-parser (xreceive)))
(if (boolean? name) (error 'sign-up "server assigned bad name") name)))
;; produce the unique names of the players signed up so far
(define/public (show-players)
(list name))
;; run a game of Acquire with the currently client player
(define/public (run _t #:show (show values))
(handle (run)
;; setup step
(send *player setup (okay 'setup-step (state-parser (xreceive))))
(xsend (void-writer))
;; accept turns, tiles, informational updates and keep queries until end message shows up
(let loop ()
(define msg (xreceive))
(cond
[(keeps-parser msg)
=> (lambda (keeps-msg)
(xsend (booleans-writer (send *player keep keeps-msg)))
(loop))]
[(turn-parser msg)
=> (lambda (turn-msg)
(define turn
(new remote-turn% [in in][out out][current-state turn-msg][player *player]))
(define-values (t h o) (send *player take-turn turn))
(if (send turn place-called)
(xsend (order-writer o))
(xsend (turn-plain-writer t h o)))
;; end of turn -- common to both
;; receive new tile
(send *player receive-tile (okay 'receive-tile (tile-parser (xreceive))))
(xsend (void-writer))
(loop))]
[(state-parser msg)
=> (lambda (inform-msg)
;; get inform message
(send *player inform inform-msg)
(xsend (void-writer))
(loop))]
[(end-parser msg)
=> (lambda (end)
(xsend (void-writer))
(values 'exhausted (rest end) `(,(first end))))]
[else (error 'run "remote proxy admin not prepared for this msg: ~e" msg)]))))))
(define remote-turn-administrator/c
(class/c
;; ------------------------------------------------------------------------------------------------
;; temporal contract
(place-called
;; how often was the place method in this turn called
(->m boolean?))))
(define/contract remote-turn%
remote-turn-administrator/c
(class/remote
(init-field current-state player)
(field
[board (state-board current-state)]
[current (state-current-player current-state)]
[cash (player-money current)]
[tiles (player-tiles current)]
[shares (state-shares current-state)]
[hotels (state-hotels current-state)]
[players (state-players current-state)])
;; -----------------------------------------------------------------------------------------------
(define *keep #f)
(define/public (reconcile-shares bad-shares)
(handle (reconcile shares)
(set! current-state (state-sub-shares current-state bad-shares))
(set! shares (state-shares current-state))))
(define/public (place-called)
*keep)
(define/public (place t h)
(handle (place t h)
(set! *keep #t)
(xsend (turn-merge-writer t h))
(xsend (booleans-writer (send player keep (okay 'keep-in-turn (keeps-parser (xreceive))))))
(okay 'place (players-parser (xreceive)))))))
;; ---------------------------------------------------------------------------------------------------
(module+ test
;; String ->* Admin IPort OPort RemotePlayer
(define (create-bundle s)
(define p0 (create "hello" ordered-s))
(define i0 (open-input-string s))
(define o0 (open-output-string))
(define r0 (new remote-administrator% [in i0][out o0]))
(values p0 i0 o0 r0))
(define-syntax-rule
(run s op)
(begin s
(let loop ((op2 (open-input-string (get-output-string op))))
(define next (read-xml-from op2))
(if (eof-object? next) '() (cons next (loop op2))))))
;; -------------------------------------------------------------------------------------------------
(define-values (p0 i0 o0 r0) (create-bundle "<signup name=\"spieker11:hello\" />"))
(check-equal? (send p0 go r0) (void))
(define-values (p1 i1 o1 r1)
(create-bundle
(with-output-to-string
(lambda ()
;; --- sign up
(define name "spieler11:hello")
(write-xml-to (signup-writer name))
;; --- set up
(define s0 (s0-name name)) ;; player owns a single tile here: A1
(write-xml-to (state-writer s0))
(define t (new turn% (current-state s0)))
;; --- turn 1
(write-xml-to (turn-writer t))
(write-xml-to (tile-writer D7))
(define s0+A1 (state-place-tile s0 A1))
(write-xml-to (state-writer s0))
;;; --- end
(write-xml-to (end-writer (state-score s0+A1) s0+A1))))))
; (read-xml-from i1)
(send p1 go r1)
(define (parse e . parsers)
(let loop ((e e) (parsers parsers))
(if (empty? parsers)
(if (null? e) (void) e)
(if ((first parsers) (first e))
(loop (rest e) (rest parsers))
(error 'parse "bad fit: ~a vs ~a" (object-name (first parsers)) (first e))))))
(parse (run (send r1 run 1) o1)
signup-parser
void-parser
turn-plain-parser
void-parser
void-parser
void-parser))