-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
1721 lines (1317 loc) · 56 KB
/
main.py
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#-*- coding: utf-8 -*-
import blessed, math, os, time, random, ai_gr_42
term = blessed.Terminal()
"""Module providing remote play features for UNamur programmation project (INFOB132).
Sockets are used to transmit orders on local or remote machines.
Firewalls or restrictive networks settings may block them.
More details on sockets: https://docs.python.org/2/library/socket.html.
Author: Benoit Frenay ([email protected]).
"""
import socket
def create_server_socket(local_port, verbose):
"""Creates a server socket.
Parameters
----------
local_port: port to listen to (int)
verbose: True if verbose (bool)
Returns
-------
socket_in: server socket (socket.socket)
"""
socket_in = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket_in.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # deal with a socket in TIME_WAIT state
if verbose:
print(' binding on local port %d to accept a remote connection' % local_port)
try:
socket_in.bind(('', local_port))
except:
raise IOError('local port %d already in use by your group or the referee' % local_port)
socket_in.listen(1)
if verbose:
print(' done -> can now accept a remote connection on local port %d\n' % local_port)
return socket_in
def create_client_socket(remote_IP, remote_port, verbose):
"""Creates a client socket.
Parameters
----------
remote_IP: IP address to send to (int)
remote_port: port to send to (int)
verbose: True if verbose (bool)
Returns
-------
socket_out: client socket (socket.socket)
"""
socket_out = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket_out.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # deal with a socket in TIME_WAIT state
connected = False
msg_shown = False
while not connected:
try:
if verbose and not msg_shown:
print(' connecting on %s:%d to send orders' % (remote_IP, remote_port))
socket_out.connect((remote_IP, remote_port))
connected = True
if verbose:
print(' done -> can now send orders to %s:%d\n' % (remote_IP, remote_port))
except:
if verbose and not msg_shown:
print(' connection failed -> will try again every 100 msec...')
time.sleep(.1)
msg_shown = True
return socket_out
def wait_for_connection(socket_in, verbose):
"""Waits for a connection on a server socket.
Parameters
----------
socket_in: server socket (socket.socket)
verbose: True if verbose (bool)
Returns
-------
socket_in: accepted connection (socket.socket)
"""
if verbose:
print(' waiting for a remote connection to receive orders')
socket_in, remote_address = socket_in.accept()
if verbose:
print(' done -> can now receive remote orders from %s:%d\n' % remote_address)
return socket_in
def create_connection(your_group, other_group=0, other_IP='127.0.0.1', verbose=False):
"""Creates a connection with a referee or another group.
Parameters
----------
your_group: id of your group (int)
other_group: id of the other group, if there is no referee (int, optional)
other_IP: IP address where the referee or the other group is (str, optional)
verbose: True only if connection progress must be displayed (bool, optional)
Returns
-------
connection: socket(s) to receive/send orders (dict of socket.socket)
Raises
------
IOError: if your group fails to create a connection
Notes
-----
Creating a connection can take a few seconds (it must be initialised on both sides).
If there is a referee, leave other_group=0, otherwise other_IP is the id of the other group.
If the referee or the other group is on the same computer than you, leave other_IP='127.0.0.1',
otherwise other_IP is the IP address of the computer where the referee or the other group is.
The returned connection can be used directly with other functions in this module.
"""
# init verbose display
if verbose:
print('\n[--- starts connection -----------------------------------------------------\n')
# check whether there is a referee
if other_group == 0:
if verbose:
print('** group %d connecting to referee on %s **\n' % (your_group, other_IP))
# create one socket (client only)
socket_out = create_client_socket(other_IP, 42000+your_group, verbose)
connection = {'in':socket_out, 'out':socket_out}
if verbose:
print('** group %d successfully connected to referee on %s **\n' % (your_group, other_IP))
else:
if verbose:
print('** group %d connecting to group %d on %s **\n' % (your_group, other_group, other_IP))
# create two sockets (server and client)
socket_in = create_server_socket(42000+your_group, verbose)
socket_out = create_client_socket(other_IP, 42000+other_group, verbose)
socket_in = wait_for_connection(socket_in, verbose)
connection = {'in':socket_in, 'out':socket_out}
if verbose:
print('** group %d successfully connected to group %d on %s **\n' % (your_group, other_group, other_IP))
# end verbose display
if verbose:
print('----------------------------------------------------- connection started ---]\n')
return connection
def bind_referee(group_1, group_2, verbose=False):
"""Put a referee between two groups.
Parameters
----------
group_1: id of the first group (int)
group_2: id of the second group (int)
verbose: True only if connection progress must be displayed (bool, optional)
Returns
-------
connections: sockets to receive/send orders from both players (dict)
Raises
------
IOError: if the referee fails to create a connection
Notes
-----
Putting the referee in place can take a few seconds (it must be connect to both groups).
connections contains two connections (dict of socket.socket) which can be used directly
with other functions in this module. connection of first (second) player has key 1 (2).
"""
# init verbose display
if verbose:
print('\n[--- starts connection -----------------------------------------------------\n')
# create a server socket (first group)
if verbose:
print('** referee connecting to first group %d **\n' % group_1)
socket_in_1 = create_server_socket(42000+group_1, verbose)
socket_in_1 = wait_for_connection(socket_in_1, verbose)
if verbose:
print('** referee succcessfully connected to first group %d **\n' % group_1)
# create a server socket (second group)
if verbose:
print('** referee connecting to second group %d **\n' % group_2)
socket_in_2 = create_server_socket(42000+group_2, verbose)
socket_in_2 = wait_for_connection(socket_in_2, verbose)
if verbose:
print('** referee succcessfully connected to second group %d **\n' % group_2)
# end verbose display
if verbose:
print('----------------------------------------------------- connection started ---]\n')
return {1:{'in':socket_in_1, 'out':socket_in_1},
2:{'in':socket_in_2, 'out':socket_in_2}}
def close_connection(connection):
"""Closes a connection with a referee or another group.
Parameters
----------
connection: socket(s) to receive/send orders (dict of socket.socket)
"""
# get sockets
socket_in = connection['in']
socket_out = connection['out']
# shutdown sockets
socket_in.shutdown(socket.SHUT_RDWR)
socket_out.shutdown(socket.SHUT_RDWR)
# close sockets
socket_in.close()
socket_out.close()
def notify_remote_orders(connection, orders):
"""Notifies orders to a remote player.
Parameters
----------
connection: sockets to receive/send orders (dict of socket.socket)
orders: orders to notify (str)
Raises
------
IOError: if remote player cannot be reached
"""
# deal with null orders (empty string)
if orders == '':
orders = 'null'
# send orders
try:
connection['out'].sendall(orders.encode())
except:
raise IOError('remote player cannot be reached')
def get_remote_orders(connection):
"""Returns orders from a remote player.
Parameters
----------
connection: sockets to receive/send orders (dict of socket.socket)
Returns
----------
player_orders: orders given by remote player (str)
Raises
------
IOError: if remote player cannot be reached
"""
# receive orders
try:
orders = connection['in'].recv(65536).decode()
except:
raise IOError('remote player cannot be reached')
# deal with null orders
if orders == 'null':
orders = ''
return orders
# Initialize data structure
def parse_map_file(path):
"""Parse the information from the cpx file.
Parameters
----------
path: path to the cpx file containing the map information (str)
Returns
-------
board_size: Size of the board (list)
antthills: Anthills's positions (list)
clods: clods's positions (list)
Version
-------
specification: Youlan Collard (v.1 26/02/21) (v.2 12/03/21)
implementation: Youlan Collard (v.1 26/02/21)
"""
fh = open(path, 'r')
lines = fh.readlines()
fh.close()
board_size = lines[1].split(' ')
anthills_pos = []
for line_index in range(3, 5):
anthill_pos = lines[line_index].split(' ')
for index in range(len(anthill_pos)):
anthill_pos[index] = int(anthill_pos[index]) - 1
anthills_pos.append(anthill_pos)
clods_info = []
for line_index_clods in range(6, len(lines)):
clod_info = lines[line_index_clods].split(' ')
for index in range(2):
clod_info[index] = int(clod_info[index]) - 1
clod_info[2] = int(clod_info[2])
clods_info.append(clod_info)
return board_size, anthills_pos, clods_info
def create_map(board_size, anthills, clods):
"""Create the data structure for the map and returns it.
Parameters
----------
board_size: Size of the game board (list)
anthills: Anthills's positions (list)
clods: clods's informations (list)
Returns
-------
main_structure: main structure for the map (list)
ant_structure: list of existing ants (list)
anthill_structure: list of 2 elements containing the anthills information (list)
Version
-------
specification: Youlan Collard (v.1 18/02/21) (v.2 26/02/21) (v.3 12/03/21)
implementation: Youlan Collard (v.1 26/02/21)
"""
main_structure = []
for y in range(int(board_size[0])):
row = []
for x in range(int(board_size[1])):
cell = {
'ant': None,
'clod': None
}
row.append(cell)
main_structure.append(row)
for clod in clods:
main_structure[clod[0]][clod[1]]['clod'] = clod[2]
anthill_structure = [
{
'team': 1,
'pos_x': anthills[0][1],
'pos_y': anthills[0][0]
},
{
'team': 2,
'pos_x': anthills[1][1],
'pos_y': anthills[1][0]
}
]
ant_structure = []
return main_structure, ant_structure, anthill_structure
# Victory function
def check_victory(main_structure, anthill_structure, number_of_turn):
"""Check if one of the player has win the game and returns the number of the team who has won.
Parameters
----------
main_structure: main structure of the game, containing the map (list)
anthill_structure: list of 2 elements containing the anthills information (list)
number_of_turn: The number of turn for this game (int)
Returns
-------
won: number of the team who has won, None if nobody has (int)
Version
-------
specification: Youlan Collard (v.1 18/02/21). Maxime Dufrasne (v.2 05/03/21)
implementation: Maxime Dufrasne (v.1 09/03/21)
"""
nbr_clod_pl_1, nbr_clod_pl_2 = check_clod(main_structure, anthill_structure)
if number_of_turn == 200:
if nbr_clod_pl_1 == nbr_clod_pl_2:
return 3
elif nbr_clod_pl_1 > nbr_clod_pl_2:
return 1
elif nbr_clod_pl_2 > nbr_clod_pl_1:
return 2
if nbr_clod_pl_1 == 8 and nbr_clod_pl_2 == 8:
return 3
elif nbr_clod_pl_1 == 8 and nbr_clod_pl_2 < 8:
return 1
elif nbr_clod_pl_1 < 8 and nbr_clod_pl_2 == 8:
return 2
else:
return None
def check_clod(main_structure, anthill_structure):
"""Check the number of clod around anthill
Parameters
----------
main_structure: main structure of the game, containing the map (list)
anthill_structure: list of 2 elements containing the anthills information (list)
Returns
-------
nbr_clod_pl_1: Number of clod player 1 has around his anthill (int)
nbr_clod_pl_2: Number of clod player 2 has around his anthill (int)
Version
--------
specification: Maxime Dufrasne (v.1 05/02/21)
implemmentation: Maxime Dufrasne (v.1 09/02/21)
"""
clod_numbers = [0, 0]
around = []
for y in range(-1, 2): # Get all the possible offset for a range of 1 around an anthill
for x in range(-1, 2):
around.append((y, x))
for pos in around:
for anthill in anthill_structure:
pos_y = pos[0] + anthill['pos_y']
pos_x = pos[1] + anthill['pos_x']
if main_structure[pos_y][pos_x]['clod']:
clod_numbers[anthill['team'] - 1] += 1
return clod_numbers[0], clod_numbers[1]
def sort_orders(orders):
"""Sort the orders by priority given by game rules
Parameters
----------
orders: list of unsorted orders (list)
Returns
-------
sorted_orders: the sorted list of orders
Version
-------
specification: Youlan Collard (v.1 25/03/21)
implementation: Youlan Collard (v.1 25/03/21)
"""
sorted_orders = []
clods_orders = []
attack_orders = []
move_orders = []
for order in orders:
if order['type'] == 'lift' or order['type'] == 'drop':
clods_orders.append(order)
elif order['type'] == 'attack':
attack_orders.append(order)
elif order['type'] == 'move':
move_orders.append(order)
# ? Pas très joli si vous avez une meilleure idée je suis preneur xD
team_1_clod, team_2_clod = seperate_team_orders(clods_orders)
sorted_orders += team_1_clod
sorted_orders += team_2_clod
team_1_attack, team_2_attack = seperate_team_orders(attack_orders)
sorted_orders += team_1_attack
sorted_orders += team_2_attack
team_1_move, team_2_move = seperate_team_orders(move_orders)
sorted_orders += team_1_move
sorted_orders += team_2_move
return sorted_orders
# Util function for sort_orders
def seperate_team_orders(orders):
"""Seperate a list of orders into two list for each teams
Parameters
----------
orders: list of orders to be seperated
Returns
-------
orders_team_1: the orders specific to team 1
orders_team_2: the orders specific to team 2
Version
-------
specification: Youlan Collard (v.1 25/03/21)
implementaion: Youlan Collard (v.1 25/03/21)
"""
team_1, team_2 = [], []
for order in orders:
if order['team'] == 1:
team_1.append(order)
else:
team_2.append(order)
return team_1, team_2
# Validation of orders
def interpret_order(main_structure, ant_structure, anthill_structure, orders):
"""Take an input, check if it's a true fonction and if it's possible, if both conditions are met, return True , if not, return False and send an error to the player.
Parameters
----------
main_structure: main structure of the game board (list)
ant_structure: structure containing all the ants (list)
anthill_structure: list of 2 elements containing the anthills information (list)
orders: the input of the user (str)
Returns
-------
order_list: the orders in a list (list)
Notes
-----
All orders should be sent in a single string with the first and second team's orders seperated by a ;
Version
-------
Specification : Letot Liam/Youlan Collard (v.1 18/02/21) (v.2 11/03/21)
implementation: Youlan Collard (v.1 11/03/21)
"""
orders_list = orders.split(';')
seems_valid = []
team_number = 0 # initialize the number team to 0
for team in orders_list:
team_number += 1 # increment it at the beginning of the loop (so it's 1 for the first iteration and 2 for the second)
for order in team.split(' '):
order_dict = {}
order_dict['team'] = team_number
if ':' in order:
order_seperated = order.split(':') # Seperate the first part of the order from the second
if '-' in order_seperated[0]:
ant_pos = order_seperated[0].split('-')
if (len(ant_pos) == 2) and (ant_pos[0].isdigit() and ant_pos[1].isdigit()):
order_dict['origin'] = (int(ant_pos[0]) - 1, int(ant_pos[1]) - 1) # -1 to both because our game board is 0 indexed and the game is 1 indexed
if order_dict['origin'][0] <= len(main_structure) and order_dict['origin'][1] <= len(main_structure[0]):
if order_seperated[1] == 'lift':
order_dict['type'] = 'lift'
order_dict['target'] = None
seems_valid.append(order_dict)
elif order_seperated[1] == 'drop':
order_dict['type'] = 'drop'
order_dict['target'] = None
seems_valid.append(order_dict)
elif "-" in order_seperated[1]:
action_pos = order_seperated[1][1:].split('-')
if (len(action_pos) == 2) and (action_pos[0].isdigit() and action_pos[1].isdigit()):
order_dict['target'] = (int(action_pos[0]) - 1, int(action_pos[1]) - 1)
if order_dict['target'][0] <= len(main_structure) and order_dict['target'][1] <= len(main_structure[0]):
if order_seperated[1][0] == '@':
order_dict['type'] = 'move'
seems_valid.append(order_dict)
elif order_seperated[1][0] == '*':
order_dict['type'] = 'attack'
seems_valid.append(order_dict)
seems_valid = sort_orders(seems_valid) # Sorting the orders before the final verification because moves actions are always sensitive to the order
valid_orders = []
for seems_valid_order in seems_valid:
origin = seems_valid_order['origin']
ant_id = main_structure[origin[0]][origin[1]]['ant']
ant = return_ant_by_id(ant_structure, ant_id)
if ant != None:
if not ant['played']:
ant['played'] = True
if seems_valid_order['type'] == 'move':
if validation_move(seems_valid_order['team'], seems_valid_order['origin'], seems_valid_order['target'], main_structure, ant_structure, anthill_structure):
already_used_square = []
for order in valid_orders:
if order['type'] == 'move':
already_used_square.append(order['target']) # Adding all already valid moves to the list
if not seems_valid_order['target'] in already_used_square: # Checking if the current move isn't already in the list
valid_orders.append(seems_valid_order)
elif seems_valid_order['type'] == 'attack':
if validation_attack(seems_valid_order['team'], main_structure, ant_structure, seems_valid_order['origin'], seems_valid_order['target']):
valid_orders.append(seems_valid_order)
elif seems_valid_order['type'] == 'lift':
if validation_lift(seems_valid_order['team'], seems_valid_order['origin'], main_structure, ant_structure):
valid_orders.append(seems_valid_order)
elif seems_valid_order['type'] == 'drop':
if validation_drop(main_structure, ant_structure, seems_valid_order['team'], seems_valid_order['origin']):
valid_orders.append(seems_valid_order)
return valid_orders
def validation_drop(main_structure, ant_structure, team, ant_pos):
"""Check if a drop action is valid
Parameters
----------
main_structure: main structure of the game board (list)
ant_structure: structure containing all the ants (list)
team: number of the team who made the order (int)
ant_pos: position of the ant executing the action (tuple)
Returns
-------
drop_valid: wether the drop action is valid (bool)
Versions
--------
specification: Youlan Collard (v.1 19/03/21)
implementation: Youlan Collard (v.1 23/03/21)
"""
ant_id = main_structure[ant_pos[0]][ant_pos[1]]['ant']
if ant_id is None:
return False
ant = return_ant_by_id(ant_structure, ant_id)
if ant['health'] > 0:
if ant['team'] == team and ant['carrying']:
return True
return False
def validation_lift(team, ant_pos, main_structure, ant_structure):
"""Check if an ant has the force to carry clod and if there is clod where it is.
Parameters
----------
team: number of the team who made the order (int)
ant_pos: position of the ant executing the action (tuple)
main_structure: main structure of the game board (list)
ant_structure: structure containing all the ants (list)
Returns
-------
lift_valid: wether the lifting action is valid or not (bool)
Version
-------
specification: Youlan Collard (v.1 21/02/21) (v.2 11/03/21)(v.3 12/03/21)
implementation: Martin Buchet (v.1 18/03/21)
"""
#TODO: For All Validation: check if the ant has already done an action this turn
lift_valid = False
ant_id = main_structure[ant_pos[0]][ant_pos[1]]['ant']
if ant_id is None:
return False
ant = return_ant_by_id(ant_structure, ant_id)
if ant['health'] > 0:
if main_structure[ant_pos[0]][ant_pos[1]]['ant'] is not None:
# check team and if ant is strong enough and if there is a clod
if team == ant['team']:
if main_structure[ant_pos[0]][ant_pos[1]]['clod']:
if ant['level'] >= main_structure[ant_pos[0]][ant_pos[1]]['clod']:
lift_valid = True
return lift_valid
def validation_attack(team, main_structure, ant_structure, attacker_pos, target_pos):
"""Check if target is in range of the attacker and return a boolean.
Parameters
----------
team: number of the team who made the order (int)
main_structure: main structure of the game board (list)
ant_structure: structure containing all the ants (list)
attacker_pos: position of attacker (tuple)
target_pos: position of target (tuple)
Return
------
is_in_range: wether the target is in range or not (bool)
Version
-------
specification: Martin Buchet (v.1 21/02/21) (v.2 11/03/21) (v.3 12/03/21)
implementation: Martin Buchet (v.1 18/03/21)
"""
# get ant_id from ant_pos then get the ant dict
ant_id = main_structure[attacker_pos[0]][attacker_pos[1]]['ant']
if ant_id == None:
return False
ant = return_ant_by_id(ant_structure, ant_id)
if ant['health'] <= 0:
return False
ant_targeted = main_structure[target_pos[0]][target_pos[1]]['ant']
if main_structure[attacker_pos[0]][attacker_pos[1]]['ant'] != None and ant_targeted != None:
# compute distance between ants
range_x = target_pos[0] - attacker_pos[0]
range_y = target_pos[1] - attacker_pos[1]
# check if the attacker ant belong to the team giving the order then check range
if team == ant['team']:
if (range_x <= 3 and range_x >= -3) and (range_y <= 3 and range_y >= -3):
return True
return False
def validation_move(team, origin, destination, main_structure, ant_structure, anthill_structure):
"""Check if deplacement is valid and return a boolean.
Parameters
----------
team: number of the team who made the order (int)
origin: depart position (tuple)
destination: destination position (tuple)
main_structure: main structure of the game board (list)
ant_structure: structure containing all the ants (list)
anthill_structure: list of 2 elements containing the anthills information (list)
Returns
-------
move_valid: wether move is valid or not (bool)
Version
-------
specification: Martin Buchet (v.1 21/02/21) (v.2 11/03/21)
implementation: Youlan Collard (v.1 12/03/21)
"""
if (destination[0] >= len(main_structure) or destination[0] < 0) or (destination[1] >= len(main_structure[0]) or destination[1] < 0): # < 0 because the order has already been converted to 0 index
return False
origin_tile = main_structure[origin[0]][origin[1]]
ant_id = origin_tile['ant']
if ant_id is None:
return False
ant = return_ant_by_id(ant_structure, ant_id)
if ant['team'] == 1 and (destination[0] == anthill_structure[1]['pos_y'] and destination[1] == anthill_structure[1]['pos_x']):
return False
elif ant['team'] == 2 and (destination[0] == anthill_structure[0]['pos_y'] and destination[1] == anthill_structure[0]['pos_x']):
return False
if ant['health'] <= 0:
return False
if ant['carrying'] and main_structure[destination[0]][destination[1]]['clod']:
return False
if ant['carrying']:
for anthill in anthill_structure:
if destination[0] == anthill['pos_y'] and destination[1] == anthill['pos_x']:
return False
if main_structure[destination[0]][destination[1]]['ant'] != None:
return False
if ant['team'] == team:
offset_origin_x = origin[0] - destination[0]
offset_origin_y = origin[1] - destination[1]
if (offset_origin_x in (-1, 0, 1)) and (offset_origin_y in (-1, 0, 1)) and not (offset_origin_x == 0 and offset_origin_y == 0):
return True
return False
# Execution of orders
def exec_order(order_list, main_structure, ant_structure, anthill_structure):
"""Execute orders and give the structures to each order fonctions.
Parameters
---------
order_list: the list of orders the user imput (list)
main_structure: main structure of the game board (list)
ant_structure: structure containing all the ants (list)
anthill_structure: list of 2 elements containing the anthills information (list)
Notes
-----
order_list has been parsed by interpret_order and is now a list of dictionnary (containing the attributes:
origin, target and type)
Version
-------
specification: Maxime Dufrasne, Liam Letot, Youlan Collard (v.1 19/02/21) (v.2 26/02/21) (v.3 11/03/21)
implementation: Youlan Collard (v.1 12/03/21)
"""
all_dead_ants = []
for order in order_list:
if order['type'] == 'move':
move(main_structure, ant_structure, order['team'], order['origin'], order['target'])
elif order['type'] == 'attack':
dead = attack(ant_structure, main_structure, order['origin'], order['target'])
if dead != None:
all_dead_ants.append(dead)
elif order['type'] == 'lift':
lift(main_structure, ant_structure, order['origin'])
elif order['type'] == 'drop':
place(main_structure, ant_structure, order['origin'], anthill_structure)
# Remove duplicated ants
confirmed_dead = []
for dead_ant in all_dead_ants:
if not dead_ant in confirmed_dead:
death((dead_ant['pos_y'], dead_ant['pos_x']), main_structure, ant_structure, dead_ant['carrying'], anthill_structure)
confirmed_dead.append(dead_ant)
def lift(main_structure, ant_structure, ant_pos):
"""Lift clod on ants.
Parameters
----------
main_structure: library of board (list)
ant_structure: library of all ants (list)
ant_pos: position of the ant that will lift clod (list)
Version
-------
specification: Maxime Dufrasne (v.1 19/02/21) (v.2 26/02/21)
implementation: Liam Letot (v.1 12/03/21)
"""
#search the id of ants in the board
ant_id = main_structure[ant_pos[0]][ant_pos[1]]['ant']
#take the ant in the ant_structure
ant = return_ant_by_id(ant_structure, ant_id)
#place the clod on the ant
ant['clod_force'] = main_structure[ant_pos[0]][ant_pos[1]]['clod']
ant['carrying'] = True
#remove the clod from the board
main_structure[ant_pos[0]][ant_pos[1]]['clod'] = None
#remove the clod on the display
lift_clod_on_display(ant_pos, ant_structure, main_structure)
def place(main_structure, ant_structure, ant_pos, anthill_structure):
"""Place clod on a case.
Parameters
----------
main_structure: library of board (list)
ant_structure: library of all ants (list)
ant_pos: position of the ant that will place clod (list)
anthill_structure: list of 2 elements containing the anthills information (list)
Version
-------
specification: Maxime Dufrasne (v.1 19/02/21) (v.2 26/02/21)
implementation: Liam Letot (v.1 12/03/21)
"""
#search the id of ants in the board
ant_id = main_structure[ant_pos[0]][ant_pos[1]]['ant']
#take the ant in the ant_structure
ant = return_ant_by_id(ant_structure, ant_id)
#place the clod on the ground
clod_force = ant['clod_force']
main_structure[ant_pos[0]][ant_pos[1]]['clod'] = ant['clod_force']
ant['carrying'] = False
#remove the clod from the ant
ant['clod_force']= None
#place the clod on the display
place_clod_on_display(ant_pos, clod_force, main_structure, ant_structure, anthill_structure)
def attack(ant_structure, main_structure, ant_pos, target_pos):
"""Compute damage done.
Parameters
----------
ant_structure: structure containing the ants (list)
main_structure: main structure of the game board (list)
ant_pos: position of the attacking ant (list)
target_pos: position of target (list)
Returns
-------
dead_ant: return the ant if it's dead (dict)
Version
-------
specification: Martin Buchet/Youlan Collard (v.1 18/02/21) (v.2 26/02/21) (v.3 12/03/21)
implementation: Liam Letot (v.1 12/03/21)
"""
#search the id of ants in the board
ant_1_id = main_structure[ant_pos[0]][ant_pos[1]]['ant']
ant_2_id = main_structure[target_pos[0]][target_pos[1]]['ant']
#take each ant in the ant_structure
ant_1 = return_ant_by_id(ant_structure, ant_1_id)
ant_2 = return_ant_by_id(ant_structure, ant_2_id)
if ant_2 == None:
return
#do the attack
ant_2['health'] -= ant_1['level']
if ant_2['health'] <0:
ant_2['health'] = 0
update_lifepoint_on_display(ant_2, ant_structure, main_structure)
if ant_2['health'] <= 0:
return ant_2
def move(main_structure, ant_structure, team, origin, destination):
"""Move the ant in main_structure and call the update of the ui
Parameters
----------
main_structure: main structure containing the game board (list)
ant_structure: structure containing the ants (list)
team: number of the team who sent the order (int)