1
1
package eu .solven .kumite .board ;
2
2
3
+ import java .util .HashSet ;
3
4
import java .util .List ;
5
+ import java .util .Map ;
6
+ import java .util .Set ;
4
7
import java .util .UUID ;
5
8
import java .util .concurrent .CountDownLatch ;
6
9
import java .util .concurrent .Executor ;
7
10
import java .util .concurrent .TimeUnit ;
8
11
import java .util .concurrent .atomic .AtomicReference ;
12
+ import java .util .random .RandomGenerator ;
9
13
import java .util .stream .Collectors ;
10
14
11
15
import org .greenrobot .eventbus .EventBus ;
12
16
13
17
import eu .solven .kumite .contest .Contest ;
18
+ import eu .solven .kumite .contest .ContestsRegistry ;
19
+ import eu .solven .kumite .events .ContestIsGameover ;
20
+ import eu .solven .kumite .events .PlayerCanMove ;
14
21
import eu .solven .kumite .events .PlayerJoinedBoard ;
15
22
import eu .solven .kumite .events .PlayerMoved ;
23
+ import eu .solven .kumite .move .IKumiteMove ;
24
+ import eu .solven .kumite .move .INoOpKumiteMove ;
16
25
import eu .solven .kumite .move .PlayerMoveRaw ;
17
26
import eu .solven .kumite .player .ContestPlayersRegistry ;
18
27
import eu .solven .kumite .player .PlayerJoinRaw ;
22
31
@ RequiredArgsConstructor
23
32
@ Slf4j
24
33
public class BoardLifecycleManager {
34
+ final ContestsRegistry contestsRegistry ;
25
35
final BoardsRegistry boardRegistry ;
26
36
27
37
final ContestPlayersRegistry contestPlayersRegistry ;
@@ -31,13 +41,15 @@ public class BoardLifecycleManager {
31
41
32
42
final EventBus eventBus ;
33
43
44
+ final RandomGenerator randomGenerator ;
45
+
34
46
/**
35
47
* When this returns, the caller is guaranteed its change has been executed
36
48
*
37
49
* @param contestId
38
50
* @param runnable
39
51
*/
40
- public void executeBoardChange (UUID contestId , Runnable runnable ) {
52
+ protected void executeBoardChange (UUID contestId , Runnable runnable ) {
41
53
if (isDirect (boardEvolutionExecutor )) {
42
54
boardEvolutionExecutor .execute (runnable );
43
55
} else {
@@ -94,29 +106,83 @@ protected static boolean isDirect(Executor executor) {
94
106
return false ;
95
107
}
96
108
97
- public void registerPlayer (Contest contest , PlayerJoinRaw playerRegistrationRaw ) {
109
+ /**
110
+ *
111
+ * @param contest
112
+ * @param playerRegistrationRaw
113
+ * @return
114
+ */
115
+ public IKumiteBoardView registerPlayer (Contest contest , PlayerJoinRaw playerRegistrationRaw ) {
98
116
UUID contestId = contest .getContestId ();
117
+ UUID playerId = playerRegistrationRaw .getPlayerId ();
118
+
119
+ AtomicReference <IKumiteBoardView > refBoardView = new AtomicReference <>();
120
+
121
+ Set <UUID > enabledPlayersIds = new HashSet <>();
122
+
99
123
executeBoardChange (contestId , () -> {
124
+ IKumiteBoard boardBefore = boardRegistry .makeDynamicBoardHolder (contestId ).get ();
125
+
126
+ Set <UUID > playerCanMoveBefore = playersCanMove (contestId , boardBefore );
127
+
100
128
// The registry takes in charge the registration in the board
101
129
contestPlayersRegistry .registerPlayer (contest , playerRegistrationRaw );
130
+
131
+ IKumiteBoard boardAfter = boardRegistry .makeDynamicBoardHolder (contestId ).get ();
132
+
133
+ Set <UUID > playerCanMoveAfter = playersCanMove (contestId , boardBefore );
134
+
135
+ // This does an intersection: players turned movable can now move, through they could not move before
136
+ enabledPlayersIds .addAll (playerCanMoveAfter );
137
+ enabledPlayersIds .removeAll (playerCanMoveBefore );
138
+
139
+ refBoardView .set (boardAfter .asView (playerId ));
102
140
});
103
141
142
+ IKumiteBoardView boardViewPostMove = refBoardView .get ();
143
+
144
+ if (boardViewPostMove == null ) {
145
+ throw new IllegalStateException ("Should have failed, or have produced a view" );
146
+ }
147
+
104
148
// We submit the event out of threadPool.
105
149
// Hence we are guaranteed the event is fully processed.
106
150
// The event subscriber can process it synchronously (through beware of deep-stack in case of long event-chains)
107
151
// Hence we do not guarantee other events interleaved when the event is processed
108
- eventBus .post (
109
- PlayerJoinedBoard .builder ().contestId (contestId ).playerId (playerRegistrationRaw .getPlayerId ()).build ());
152
+ eventBus .post (PlayerJoinedBoard .builder ().contestId (contestId ).playerId (playerId ).build ());
153
+
154
+ enabledPlayersIds .forEach (enabledPlayerId -> {
155
+ eventBus .post (PlayerCanMove .builder ().contestId (contestId ).playerId (playerId ).build ());
156
+ });
157
+
158
+ return boardViewPostMove ;
159
+ }
160
+
161
+ private Set <UUID > playersCanMove (UUID contestId , IKumiteBoard board ) {
162
+ Contest contest = contestsRegistry .getContest (contestId );
163
+
164
+ Set <UUID > movablePlayerIds = board .snapshotPlayers ()
165
+ .stream ()
166
+ .filter (playerId -> canPlay (
167
+ contest .getGame ().exampleMoves (randomGenerator , board .asView (playerId ), playerId )))
168
+ .collect (Collectors .toSet ());
169
+
170
+ return movablePlayerIds ;
171
+ }
172
+
173
+ private boolean canPlay (Map <String , IKumiteMove > exampleMoves ) {
174
+ return exampleMoves .values ().stream ().anyMatch (move -> !(move instanceof INoOpKumiteMove ));
110
175
}
111
176
112
177
public IKumiteBoardView onPlayerMove (Contest contest , PlayerMoveRaw playerMove ) {
113
178
UUID contestId = contest .getContestId ();
179
+ UUID playerId = playerMove .getPlayerId ();
114
180
115
- AtomicReference <IKumiteBoardView > refBoardView = new AtomicReference <>();
181
+ AtomicReference <IKumiteBoard > refBoard = new AtomicReference <>();
116
182
117
- executeBoardChange (contestId , () -> {
118
- UUID playerId = playerMove .getPlayerId ();
183
+ Set <UUID > enabledPlayersIds = new HashSet <>();
119
184
185
+ executeBoardChange (contestId , () -> {
120
186
if (!contestPlayersRegistry .isRegisteredPlayer (contestId , playerId )) {
121
187
List <UUID > contestPlayers = contestPlayersRegistry .makeDynamicHasPlayers (contestId )
122
188
.getPlayers ()
@@ -132,6 +198,8 @@ public IKumiteBoardView onPlayerMove(Contest contest, PlayerMoveRaw playerMove)
132
198
133
199
IKumiteBoard currentBoard = boardRegistry .makeDynamicBoardHolder (contestId ).get ();
134
200
201
+ Set <UUID > playerCanMoveBefore = playersCanMove (contestId , currentBoard );
202
+
135
203
// First `.checkMove`: these are generic checks (e.g. is the gamerOver?)
136
204
try {
137
205
contest .checkValidMove (playerMove );
@@ -144,20 +212,35 @@ public IKumiteBoardView onPlayerMove(Contest contest, PlayerMoveRaw playerMove)
144
212
// This may still fail (e.g. the move is illegal given game rules)
145
213
currentBoard .registerMove (playerMove );
146
214
215
+ Set <UUID > playerCanMoveAfter = playersCanMove (contestId , currentBoard );
216
+
217
+ // This does an intersection: players turned movable can now move, through they could not move before
218
+ enabledPlayersIds .addAll (playerCanMoveAfter );
219
+ enabledPlayersIds .removeAll (playerCanMoveBefore );
220
+
147
221
// Persist the board (e.g. for concurrent changes)
148
222
boardRegistry .updateBoard (contestId , currentBoard );
149
223
150
- refBoardView .set (currentBoard . asView ( playerId ) );
224
+ refBoard .set (currentBoard );
151
225
152
226
});
153
227
154
- IKumiteBoardView boardViewPostMove = refBoardView .get ();
228
+ IKumiteBoard boardAfter = refBoard .get ();
229
+ IKumiteBoardView boardViewPostMove = boardAfter .asView (playerId );
155
230
156
231
if (boardViewPostMove == null ) {
157
232
throw new IllegalStateException ("Should have failed, or have produced a view" );
158
233
}
159
234
160
- eventBus .post (PlayerMoved .builder ().contestId (contestId ).playerId (playerMove .getPlayerId ()).build ());
235
+ eventBus .post (PlayerMoved .builder ().contestId (contestId ).playerId (playerId ).build ());
236
+
237
+ enabledPlayersIds .forEach (enabledPlayerId -> {
238
+ eventBus .post (PlayerCanMove .builder ().contestId (contestId ).playerId (playerId ).build ());
239
+ });
240
+
241
+ if (contest .getGame ().makeDynamicGameover (() -> boardAfter ).isGameOver ()) {
242
+ eventBus .post (ContestIsGameover .builder ().contestId (contestId ).build ());
243
+ }
161
244
162
245
return boardViewPostMove ;
163
246
}
0 commit comments