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 }