1 module serverlogic; 2 3 import core.thread : Fiber; 4 import std.stdio : writeln, writefln; 5 import std.container : DList; 6 7 import connection; 8 import server; 9 import packets; 10 11 import gameboard; 12 13 struct Player 14 { 15 ClientId clientId; 16 17 uint numShips; 18 uint score; 19 } 20 21 enum ActionType 22 { 23 // - no packet data, + with packet data 24 /*-*/ ready, 25 /*-*/ unready, 26 /*+*/ plan, 27 /*-*/ deployShips 28 } 29 30 struct Action 31 { 32 ActionType type; 33 ClientId clientId; 34 ubyte[] packetData; 35 } 36 37 import std.range : isInputRange; 38 auto getPopFront(IR)(ref IR inputRange) 39 if (isInputRange!IR) 40 { 41 import std.range : front, popFront; 42 auto frontItem = inputRange.front; 43 inputRange.popFront(); 44 return frontItem; 45 } 46 47 class ServerLogicFiber : Fiber 48 { 49 DList!Action* queue; 50 Server server; 51 52 Action waitForAction() 53 { 54 while (queue.empty) 55 { 56 Fiber.yield(); 57 } 58 auto item = queue.front; 59 queue.removeFront; 60 return item; 61 } 62 63 this(DList!Action* queue, Server server) 64 { 65 assert(queue && server); 66 67 this.queue = queue; 68 this.server = server; 69 super(&run); 70 } 71 72 Client* getClient(ClientId id) 73 { 74 return server.clientStorage.clients[id]; 75 } 76 77 // LOGIC 78 79 GameBoard board; 80 ClientId[] players; 81 ClientId triPrimeOwner; // 0 if none 82 83 void rotatePlayers() 84 { 85 import std.algorithm : bringToFront; 86 bringToFront(players[0..1], players[1..$]); 87 } 88 89 void run() 90 { 91 waitForReady(); 92 board = boardGen(); 93 94 BoardDataPacket packet; 95 packet.systemLevels = new uint[54]; 96 foreach(i, hex; board.data) 97 packet.systemLevels[i] = hex.systemLevel; 98 server.sendToAll(packet); 99 100 deployShips(); 101 rounds(); 102 endGame(); 103 server.isRunning = false; 104 } 105 106 GameBoard boardGen() 107 { 108 import std.random : randomShuffle, dice; 109 110 GameBoard board; 111 112 board.triPrime.systemLevel = 3; 113 114 alias BorderSector = uint[5]; 115 116 BorderSector[6] borders = 117 [ 118 [2,0,1,0,1], 119 [1,0,1,2,0], 120 [1,1,0,0,2], 121 [0,0,2,1,1], 122 [1,0,2,1,0], 123 [1,1,2,0,0], 124 ]; 125 126 randomShuffle(borders[]); 127 128 // place top hexes 129 foreach(i; 0..3) 130 { 131 board[i*2, 0].systemLevel = borders[i][0]; 132 board[i*2 + 1, 0].systemLevel = borders[i][1]; 133 board[i*2, 1].systemLevel = borders[i][2]; 134 board[i*2, 2].systemLevel = borders[i][3]; 135 board[i*2 + 1, 2].systemLevel = borders[i][4]; 136 } 137 138 // place bottom hexes 139 foreach(i; 0..3) 140 { 141 board[i*2, 6].systemLevel = borders[i+3][4]; 142 board[i*2 + 1, 6].systemLevel = borders[i+3][3]; 143 board[i*2, 7].systemLevel = borders[i+3][2]; 144 board[i*2, 8].systemLevel = borders[i+3][1]; 145 board[i*2 + 1, 8].systemLevel = borders[i+3][0]; 146 } 147 148 alias MiddleSector = uint[4]; 149 MiddleSector[2] middles = [ 150 [2,1,0,1], [1,0,2,1] 151 ]; 152 immutable indexes = [ 153 [0,1,2,3], 154 [3,2,1,0], 155 ]; 156 157 randomShuffle(middles[]); 158 159 // Left middle piece 160 auto rand = dice(1, 1); // 0 or 1. Select rotation of sector 161 board[0, 3].systemLevel = middles[0][ indexes[rand][0] ]; 162 board[0, 4].systemLevel = middles[0][ indexes[rand][1] ]; 163 board[1, 4].systemLevel = middles[0][ indexes[rand][2] ]; 164 board[0, 5].systemLevel = middles[0][ indexes[rand][3] ]; 165 166 // Right middle piece 167 rand = dice(1, 1); // 0 or 1. Select rotation of sector 168 board[4, 3].systemLevel = middles[1][ indexes[rand][0] ]; 169 board[4, 4].systemLevel = middles[1][ indexes[rand][1] ]; 170 board[5, 4].systemLevel = middles[1][ indexes[rand][2] ]; 171 board[4, 5].systemLevel = middles[1][ indexes[rand][3] ]; 172 173 return board; 174 } 175 176 void waitForReady() 177 { 178 int numReady; 179 180 while (numReady < 3) 181 { 182 Action a = waitForAction(); 183 184 if (a.type == ActionType.ready && !getClient(a.clientId).isReady) 185 { 186 getClient(a.clientId).isReady = true; 187 players ~= a.clientId; 188 ++numReady; 189 } 190 else if (a.type == ActionType.unready && getClient(a.clientId).isReady) 191 { 192 import std.algorithm : remove; 193 getClient(a.clientId).isReady = false; 194 players = remove!(c => c == a.clientId)(players); 195 --numReady; 196 } 197 } 198 } 199 200 void deployShip(ClientId playerId, bool[] occupiedSectors) 201 { 202 uint[] freeSectors; 203 foreach (i, s; occupiedSectors) 204 if (!s) freeSectors ~= i; 205 206 bool isValidAction(Action a) 207 { 208 return a.type == ActionType.deployShips && a.clientId == playerId; 209 } 210 211 while(true) // Wait for deployShips action 212 { 213 server.sendTo(playerId, DeployShipsArgsPacket(freeSectors)); 214 215 Action action; 216 do 217 { 218 action = waitForAction(); 219 writefln("%s %s", playerId, action.type); 220 } 221 while (!isValidAction(action)); 222 223 auto packet = server.unpackPacket!DeployShipsResultPacket(action.packetData); 224 uint sector = sectorNumber(HexCoords(cast(ubyte)packet.x, cast(ubyte)packet.y)); 225 226 if (packet.x >= boardWidth || packet.y >= boardHeight) continue; 227 if (sector == 4 || sector >= 9) continue; 228 if (occupiedSectors[sector]) continue; 229 if (board[packet.x, packet.y].systemLevel != 1) continue; 230 231 occupiedSectors[sector] = true; 232 233 board[packet.x, packet.y].playerId = playerId; 234 board[packet.x, packet.y].numShips = 2; 235 getClient(playerId).numShips += 2; 236 237 server.sendToAll(HexDataPacket(packet.x, packet.y, playerId, 2)); 238 break; 239 } 240 } 241 242 void deployShips() 243 { 244 import std.range : chain, retro; 245 246 bool[9] occupiedSectors; 247 occupiedSectors[4] = true; 248 249 foreach(playerId; chain(players, players.retro)) 250 { 251 server.sendToAll(ClientTurnPacket(ClientTurn.deployShips, playerId)); 252 deployShip(playerId, occupiedSectors); 253 } 254 } 255 256 void rounds() 257 { 258 foreach(_; 0..numRounds) 259 round(); 260 } 261 262 void round() 263 { 264 writeln("Plan"); 265 plan(); 266 267 writeln("Perform"); 268 perform(); 269 270 exploit(); 271 272 rotatePlayers(); 273 } 274 275 // phase 1 276 void plan() 277 { 278 server.sendToAll(ClientTurnPacket(ClientTurn.plan, 0)); 279 280 uint numPlayersDonePlan; 281 while(numPlayersDonePlan < players.length) 282 { 283 Action action = waitForAction(); 284 285 if (action.type != ActionType.plan) continue; // reject not valid action. 286 if (getClient(action.clientId).commands.length != 0) continue; // reject, commands already set. 287 288 auto packet = server.unpackPacket!PlanResultPacket(action.packetData); 289 290 if (packet.commands.length != 3) // Invalid result. 291 { 292 server.sendTo(action.clientId, ClientTurnPacket(ClientTurn.plan, 0)); 293 continue; 294 } 295 296 getClient(action.clientId).commands = packet.commands; 297 298 ++numPlayersDonePlan; 299 } 300 301 foreach(pid; players) 302 writefln("%s %(%s -> %)", pid, getClient(pid).commands); 303 } 304 305 static struct PlayerCommand 306 { 307 Command command; 308 ClientId playerId; 309 uint numTurns; // 1-3 310 } 311 312 // phase 2 313 void perform() 314 { 315 import std.algorithm : count, sort, SwapStrategy; 316 import std.range : moveFront; 317 318 foreach(_; 0..3) 319 { 320 // Get first command of all players. 321 PlayerCommand[] playerCommands; 322 foreach(player; players) 323 { 324 playerCommands ~= PlayerCommand(getClient(player).commands.getPopFront, player); 325 } 326 327 sort!("a.command < b.command", SwapStrategy.stable)(playerCommands); 328 foreach(ref command; playerCommands) 329 { 330 command.numTurns = 4 - count!((a, b) => a.command == b.command)(playerCommands, command); 331 } 332 333 foreach(playerCommand; playerCommands) 334 foreach(__; 0..playerCommand.numTurns) 335 final switch(playerCommand.command) 336 { 337 case Command.expand: 338 expand(playerCommand.playerId); 339 break; 340 case Command.explore: 341 explore(playerCommand.playerId); 342 break; 343 case Command.exterminate: 344 exterminate(playerCommand.playerId); 345 break; 346 } 347 } 348 } 349 350 void expand(ClientId playerId) 351 { 352 writefln("expand %s", playerId); 353 return; 354 //server.sendToAll(ClientTurnPacket(ClientTurn.expand, playerId)); 355 } 356 357 void explore(ClientId playerId) 358 { 359 writefln("explore %s", playerId); 360 return; 361 //server.sendToAll(ClientTurnPacket(ClientTurn.explore, playerId)); 362 } 363 364 void exterminate(ClientId playerId) 365 { 366 writefln("exterminate %s", playerId); 367 return; 368 //server.sendToAll(ClientTurnPacket(ClientTurn.exterminate, playerId)); 369 } 370 371 // phase 3 372 void exploit() 373 { 374 sustainShips(); 375 } 376 377 void sustainShips() 378 { 379 foreach(hex; board.data) 380 { 381 if (hex.numShips > hex.systemLevel + 1) 382 { 383 hex.numShips = hex.systemLevel + 1; 384 } 385 } 386 } 387 388 void endGame() 389 { 390 import std.algorithm : sort; 391 392 uint maxScorePlayer, maxScore; 393 394 foreach(p; players) 395 { 396 if (getClient(p).score > maxScore) 397 { 398 maxScorePlayer = p; 399 maxScore = getClient(p).score; 400 } 401 } 402 403 writefln("%s won with %s points", maxScorePlayer, maxScore); 404 } 405 }