diff --git a/Test_case/level_3/12.json b/Test_case/level_3/12.json new file mode 100644 index 0000000000000000000000000000000000000000..37334dc7b7992dc42bceef67f62493c84469db98 --- /dev/null +++ b/Test_case/level_3/12.json @@ -0,0 +1,103 @@ +{ + "upper": [ + [ + "p", + 0, + -3 + ], + [ + "s", + 0, + 0 + ] + ], + "lower": [ + [ + "r", + 0, + 1 + ], + [ + "p", + 0, + 2 + ] + ], + "block": [ + [ + "", + -1, + -3 + ], + [ + "", + -1, + -2 + ], + [ + "", + -1, + -1 + ], + [ + "", + -1, + 0 + ], + [ + "", + -1, + 1 + ], + [ + "", + -1, + 2 + ], + [ + "", + -1, + 3 + ], + [ + "", + 0, + 3 + ], + [ + "", + 1, + -4 + ], + [ + "", + 1, + -3 + ], + [ + "", + 1, + -2 + ], + [ + "", + 1, + -1 + ], + [ + "", + 1, + 0 + ], + [ + "", + 1, + 1 + ], + [ + "", + 1, + 2 + ] + ] +} \ No newline at end of file diff --git a/search/__pycache__/main.cpython-38.pyc b/search/__pycache__/main.cpython-38.pyc index 4796c0c303b7134efd9eeda4d2d15bd9c1c39d38..1ac4f3a1eb06acd9bcb68a9797aeb04de23944bd 100644 Binary files a/search/__pycache__/main.cpython-38.pyc and b/search/__pycache__/main.cpython-38.pyc differ diff --git a/search/__pycache__/search_algo.cpython-38.pyc b/search/__pycache__/search_algo.cpython-38.pyc index 38e12487fceb0dbd91d980333270203f9d429110..9c6190551c3a2812e7abaca1b7123fa02b9eb34a 100644 Binary files a/search/__pycache__/search_algo.cpython-38.pyc and b/search/__pycache__/search_algo.cpython-38.pyc differ diff --git a/search/main.py b/search/main.py index 59181f27d05a476318acab91f34e824174141309..d463cb96a92a52797c817800fd5ee67096ef59ee 100644 --- a/search/main.py +++ b/search/main.py @@ -8,14 +8,8 @@ This script contains the entry point to the program (the code in import sys import json -from search.movement_logic import * from search.search_algo import * -# Constant's definition. - - - - def main(): # define global variable @@ -47,7 +41,8 @@ def main(): input() upperDictPieces = update_state(upperDictPieces, lowerDictPieces, setBlocks, targetDict) targetDict = check_if_piece_hit_target(upperDictPieces, lowerDictPieces, targetDict) - print(lowerDictPieces) + + print_board(make_board(lowerDictPieces,upperDictPieces,setBlocks)) def parse_input(data): """ @@ -101,40 +96,7 @@ def parse_input(data): setBlocks.add((block[ROW], block[COLUMN])) -def piece_collision(pieceA, pieceB) -> int: - """ - Our upper pieces are R, P and S, lower pieces are r, p and s - We will convert both to lower case letters and check each case - Would be nice to use comparator but can't have r>s>p>r using it - - Return A_WIN if pieceA wins, B_WIN if pieceB wins and DRAW if neither win - Only look at first character of string - :param pieceA: type of the token in {'R','P','S','r','p','s'} - :param pieceB: type of the token in {'R','P','S','r','p','s'} - :return: A_WIN, B_WIN or DRAW - """ - pieceA = pieceA[TYPE].lower() - pieceB = pieceB[TYPE].lower() - if pieceA == "r": - if pieceB == "s": - return A_WIN - elif pieceB == "p": - return B_WIN - - elif pieceA == "s": - if pieceB == "p": - return A_WIN - elif pieceB == "r": - return B_WIN - - elif pieceA == "p": - if pieceB == "r": - return A_WIN - elif pieceB == "s": - return B_WIN - - return DRAW diff --git a/search/search_algo.py b/search/search_algo.py index 9d9519c79c39d7cc2532d05e4612a2bc3d86d4fe..5b9dfef56458f4720f00fb1d3bae0cb3e926e628 100644 --- a/search/search_algo.py +++ b/search/search_algo.py @@ -1,8 +1,8 @@ -from search.movement_logic import slide_right, slide_left, slide_up_left, slide_up_right, slide_down_left, \ - slide_down_right, compare_tile, distance_between, swing_to_tile_1, swing_to_tile_2, swing_to_tile_3 +from search.movement_logic import * from search.util import print_board -BLOCK = "" +# BLOCK should be "" but put [X] so we can observe from the print_board statement +BLOCK = "[X]" POSITION_CLOSEST_TO_TARGET = 0 TYPE = 0 ROW = 1 @@ -16,9 +16,10 @@ FIRST_CHAR = 0 upperDictPieces = {} lowerDictPieces ={} targetDict = {} - +targeted_l_piece = set() setBlocks = set() + def make_board(lowerPieces, upperPieces, setBlocks): """ create a board of the current game -> can do a position look up. @@ -124,11 +125,13 @@ def make_priority_list_of_action(upperDict, piece, targetDict, board): """ # add all the adjacent move to queue position_list = add_slide_action(upperDict, piece, board) + + # add all the swing action to queue position_list.extend(add_swing_action(upperDict, piece, board)) - #print(position_list) # sort the list base on the how close it is to target - position_list.sort(key=(lambda x: distance_between(x, targetDict[piece]))) + if piece in targetDict.keys(): + position_list.sort(key=(lambda x: distance_between(x, targetDict[piece]))) return position_list @@ -146,15 +149,71 @@ def update_state(upperPieces, lowerPieces, setBlocks, targetDict): # create the board in order to support faster look up board = make_board(lowerPieces, upperPieces, setBlocks) print_board(board) - + possible_action = {} # right now, i assume there only 1 piece, there will be more code combine all the queue. for piece in upperPieces: - position_list = make_priority_list_of_action(upperPieces, piece, targetDict, board) - upperPieces[piece] = position_list[POSITION_CLOSEST_TO_TARGET] + possible_action[piece] = make_priority_list_of_action(upperPieces, piece, targetDict, board) + + upperPieces = choose_optimal_combination(possible_action, upperPieces, board, targetDict) + return upperPieces + + +def choose_optimal_combination(possible_action, upperPieces, board, targetDict): + + """ + prioritise action lead a piece directly to its target. + else choose a combination that does not cause collision between upper piece + + :param possible_action: all set of possible move sorted base on how close they are to the piece target + :param upperPieces: contain all upper piece + :param board: all piece + :param targetDict: map all piece to its target + :return: + """ + finished = set() + for piece in possible_action.keys(): + if piece in possible_action.keys(): + + if piece in targetDict.keys() and compare_tile(possible_action[piece][0], targetDict[piece]): + + # perform swap and update board + del board[upperPieces[piece]] + upperPieces[piece] = targetDict[piece] + + board[targetDict[piece]] = piece + # we've finished with this piece. + finished.add(piece) + + # each piece is moved once and only once. + for piece in possible_action: + if piece in finished: + continue + index = 0 + moved = False + while not moved: + # check if the move exist in the board + if possible_action[piece][index] not in board.keys(): + # perform swap and update board + del board[upperPieces[piece]] + upperPieces[piece] = possible_action[piece][index] + + board[possible_action[piece][index]] = piece + moved = True + else: + # check if no collision happen: + if piece_collision(board[possible_action[piece][index]], piece) == DRAW: + # perform swap and update board + del board[upperPieces[piece]] + upperPieces[piece] = possible_action[piece][index] + + board[possible_action[piece][index]] = piece + moved = True + index += 1 return upperPieces + def check_if_piece_hit_target(upperPieces, lowerPieces, targetDict): """ remove the target from the target dictionary if the upper piece is at its target location @@ -164,7 +223,7 @@ def check_if_piece_hit_target(upperPieces, lowerPieces, targetDict): """ removed = False for piece in upperPieces: - if targetDict and compare_tile(upperPieces[piece], targetDict[piece]): + if piece in targetDict.keys() and compare_tile(upperPieces[piece], targetDict[piece]): for removed_piece in lowerPieces: if compare_tile(lowerPieces[removed_piece],targetDict[piece]): removed = removed_piece @@ -185,6 +244,42 @@ def check_if_piece_hit_target(upperPieces, lowerPieces, targetDict): return targetDict +def piece_collision(pieceA, pieceB) -> int: + """ + Our upper pieces are R, P and S, lower pieces are r, p and s + We will convert both to lower case letters and check each case + Would be nice to use comparator but can't have r>s>p>r using it + + Return A_WIN if pieceA wins, B_WIN if pieceB wins and DRAW if neither win + Only look at first character of string + + :param pieceA: type of the token in {'R','P','S','r','p','s'} + :param pieceB: type of the token in {'R','P','S','r','p','s'} + :return: A_WIN, B_WIN or DRAW + """ + pieceA = pieceA[TYPE].lower()[0] + pieceB = pieceB[TYPE].lower()[0] + if pieceA == "r": + if pieceB == "s": + return A_WIN + elif pieceB == "p": + return B_WIN + + elif pieceA == "s": + if pieceB == "p": + return A_WIN + elif pieceB == "r": + return B_WIN + + elif pieceA == "p": + if pieceB == "r": + return A_WIN + elif pieceB == "s": + return B_WIN + + return DRAW + + def add_swing_action(upperDict, piece, board): """ check for adjacent tile. @@ -198,37 +293,37 @@ def add_swing_action(upperDict, piece, board): # check the right tile right_tile = slide_right(upperDict[piece]) if not compare_tile(right_tile, upperDict[piece]) and\ - right_tile in board and board[right_tile] != "": + right_tile in board and board[right_tile] != BLOCK: adjacent_list.append(right_tile) # check the left tile left_tile = slide_left(upperDict[piece]) if not compare_tile(left_tile, upperDict[piece]) and\ - left_tile in board and board[left_tile] != "": + left_tile in board and board[left_tile] != BLOCK: adjacent_list.append(left_tile) # check the up_left up_left_tile = slide_up_left(upperDict[piece]) if not compare_tile(up_left_tile, upperDict[piece]) and\ - up_left_tile in board and board[up_left_tile] != "": + up_left_tile in board and board[up_left_tile] != BLOCK: adjacent_list.append(up_left_tile) # check the up_right up_right_tile = slide_up_right(upperDict[piece]) if not compare_tile(up_right_tile, upperDict[piece]) and\ - up_right_tile in board and board[up_right_tile] != "": + up_right_tile in board and board[up_right_tile] != BLOCK: adjacent_list.append(up_right_tile) # check the down_left down_left_tile = slide_down_left(upperDict[piece]) if not compare_tile(down_left_tile, upperDict[piece]) and\ - down_left_tile in board and board[down_left_tile] != "": + down_left_tile in board and board[down_left_tile] != BLOCK: adjacent_list.append(down_left_tile) # check the down_right down_right_tile = slide_down_right(upperDict[piece]) if not compare_tile(down_right_tile, upperDict[piece]) and\ - down_right_tile in board and board[down_right_tile] != "": + down_right_tile in board and board[down_right_tile] != BLOCK: adjacent_list.append(down_right_tile) # if there are at least one adjacent piece : add the adjacent move from those tile @@ -271,9 +366,8 @@ def find_target(piece_key): # Start with dummy information targetPiece = "" targetDist = MAX_DISTANCE - print(lowerDictPieces) for key in lowerDictPieces: - if key[FIRST_CHAR] == target: + if key[FIRST_CHAR] == target and key not in targeted_l_piece: distance = distance_between(upperDictPieces[piece_key], lowerDictPieces[key]) # if the distance between this key and the query key is less than the least distance. # as well as the key is not targeted by any other key. @@ -287,4 +381,5 @@ def find_target(piece_key): return else: targetDict[piece_key] = lowerDictPieces[targetPiece] + targeted_l_piece.add(targetPiece)