├── .gitignore ├── .settings └── .gitignore ├── README.md ├── config ├── .gitignore ├── Boss.java ├── config.ini ├── level1 │ └── Boss.py3 ├── level2 │ ├── Boss.py3 │ ├── welcome_en.html │ └── welcome_fr.html ├── level3 │ ├── welcome_en.html │ └── welcome_fr.html ├── statement_en.html.tpl ├── statement_fr.html.tpl └── stub.txt ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── codingame │ │ ├── game │ │ ├── InputActions │ │ │ ├── Action.java │ │ │ ├── InvalidAction.java │ │ │ ├── MoveAction.java │ │ │ ├── PassAction.java │ │ │ └── PushAction.java │ │ ├── Model │ │ │ ├── AbstractModel.java │ │ │ ├── CardModel.java │ │ │ ├── GameBoard.java │ │ │ ├── Item.java │ │ │ ├── MovingModel.java │ │ │ ├── PlayerModel.java │ │ │ ├── StateUpdates │ │ │ │ ├── CardPositionUpdate.java │ │ │ │ ├── FlipCardUpdate.java │ │ │ │ ├── PoppedUpdate.java │ │ │ │ ├── PushedUpdate.java │ │ │ │ ├── RemoveCardUpdate.java │ │ │ │ ├── RemoveItemUpdate.java │ │ │ │ └── ShowFrameUpdate.java │ │ │ └── TileModel.java │ │ ├── Player.java │ │ ├── Referee.java │ │ ├── Utils │ │ │ ├── Constants.java │ │ │ └── Vector2.java │ │ └── View │ │ │ ├── AbstractView.java │ │ │ ├── ArrowView.java │ │ │ ├── BoardView.java │ │ │ ├── CardDeckView.java │ │ │ ├── CardView.java │ │ │ ├── MovingView.java │ │ │ ├── PlayerTextView.java │ │ │ ├── PlayerView.java │ │ │ ├── TileView.java │ │ │ ├── TurnTextView.java │ │ │ └── ViewController.java │ │ └── view │ │ ├── endscreen │ │ └── EndScreenModule.java │ │ ├── nicknameshandler │ │ └── NicknamesHandlerModule.java │ │ └── tooltip │ │ └── TooltipModule.java └── resources │ └── view │ ├── assets │ ├── arrow_0.png │ ├── arrow_1.png │ ├── arrow_2.png │ ├── background.jpg │ ├── background_name_0.png │ ├── background_name_1.png │ ├── cardBack_0.png │ ├── cardBack_1.png │ ├── cardFront_0.png │ ├── cardFront_1.png │ ├── elf_0.png │ ├── elf_1.png │ ├── frame.png │ ├── items_sheet.json │ ├── items_sheet.png │ ├── logo.png │ ├── tile_background.png │ ├── tile_decorators.json │ ├── tile_decorators.png │ ├── tile_paths.json │ └── tile_paths.png │ ├── config.js │ ├── demo.js │ └── modules │ ├── endScreen │ └── EndScreenModule.js │ ├── nicknamesHandlerModule │ └── NicknamesHandlerModule.js │ ├── toggleModule │ └── ToggleModule.js │ └── tooltip │ └── TooltipModule.js └── test ├── java ├── Main.java ├── Player1.java ├── Player2.java └── com │ └── codingame │ └── game │ ├── InputActions │ ├── MoveActionTest.java │ ├── PassActionTest.java │ └── PushActionTest.java │ ├── Model │ ├── CardModelTest.java │ ├── ItemTest.java │ ├── PlayerModelTest.java │ └── TileModelTest.java │ ├── PlayerTest.java │ └── Utils │ └── Vector2Test.java └── resources └── log4j2.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .factorypath 2 | .settings/ 3 | /target/ 4 | /.project 5 | /.classpath 6 | .idea/ 7 | *.DS_Store 8 | *.iml -------------------------------------------------------------------------------- /.settings/.gitignore: -------------------------------------------------------------------------------- 1 | /org.eclipse.jdt.core.prefs 2 | /org.eclipse.m2e.core.prefs 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Repo for the Xmas Rush contest on CodinGame 2 | -------------------------------------------------------------------------------- /config/.gitignore: -------------------------------------------------------------------------------- 1 | statement_en.html 2 | statement_fr.html -------------------------------------------------------------------------------- /config/Boss.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | class Player { 4 | public static void main(String args[]) { 5 | Scanner in = new Scanner(System.in); 6 | int boardWidth = 7; 7 | int boardHeight = 7; 8 | 9 | int turn = 0; 10 | // game loop 11 | while (true) { 12 | int turnType = in.nextInt(); 13 | for (int i = 0; i < boardHeight; i++) { 14 | for (int j = 0; j < boardWidth; j++) { 15 | String tile = in.next(); 16 | System.err.print(tile + " "); 17 | } 18 | System.err.println(); 19 | } 20 | for (int i = 0; i < 2; i++) { 21 | int numPlayerCards = in.nextInt(); // the number of cards in the stack for each player 22 | int playerX = in.nextInt(); 23 | int playerY = in.nextInt(); 24 | String playerTile = in.next(); 25 | System.err.println(numPlayerCards + " " + playerX + "," + playerY + " " + playerTile); 26 | } 27 | int numItems = in.nextInt(); // the total number of items available on board and on player tiles (does not include quest cards) 28 | for (int i = 0; i < numItems; i++) { 29 | String itemName = in.next(); 30 | int itemX = in.nextInt(); 31 | int itemY = in.nextInt(); 32 | int itemPlayerId = in.nextInt(); 33 | System.err.println(itemName + itemPlayerId + " " + itemX + "," + itemY); 34 | } 35 | int numQuests = in.nextInt(); // the total number of available quest cards for both players 36 | for (int i = 0; i < numQuests; i++) { 37 | String questItemName = in.next(); 38 | int questPlayerId = in.nextInt(); 39 | System.err.println(questItemName + questPlayerId); 40 | } 41 | 42 | if (turnType == 0) { 43 | System.out.println("PUSH 3 DOWN"); 44 | } else { 45 | System.out.println("PASS"); 46 | } 47 | turn++; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /config/config.ini: -------------------------------------------------------------------------------- 1 | title=Xmas Rush 2 | type=multi 3 | min_players=2 4 | max_players=2 5 | -------------------------------------------------------------------------------- /config/level1/Boss.py3: -------------------------------------------------------------------------------- 1 | import sys 2 | import math 3 | from collections import namedtuple 4 | from collections import deque 5 | 6 | Vector = namedtuple('Vector', ['x', 'y']) 7 | Step = namedtuple('Step', ['direction', 'tile']) 8 | Item = namedtuple('Item', ['name','position']) 9 | 10 | boardWidth, boardHeight = 7, 7 11 | 12 | # Here are my global variables, these keep their values between turns 13 | column = 0 14 | row = 0 15 | toggle = True 16 | moveType = 0 17 | 18 | # I don't know if i'm player one or not yet 19 | player = -1 20 | 21 | # A dictionnary linking directions to their vectors 22 | moves = { 23 | 'LEFT': Vector(-1, 0), 24 | 'UP': Vector(0, -1), 25 | 'RIGHT': Vector(1, 0), 26 | 'DOWN': Vector(0, 1) 27 | } 28 | # A dictionnary linking directions to their opposite 29 | oppositeMove = { 30 | 'LEFT': 'RIGHT', 31 | 'UP': 'DOWN', 32 | 'RIGHT': 'LEFT', 33 | 'DOWN': 'UP' 34 | } 35 | 36 | # Here is a function allowing me to add two vectors 37 | def addPositions(v1, v2): 38 | return Vector(v1.x + v2.x, v1.y + v2.y) 39 | 40 | # Now we're going to create our tile object 41 | class Tile: 42 | 43 | # This function allows us to pass parameters when creating the object 44 | def __init__(self, pos, code): 45 | self.pos = pos # the coordinates of the tile 46 | # the directions that you can take from this tile 47 | self.availableMoves = self.parseCode(code) 48 | 49 | # this function allows us to use the '0101' code of the tile and turn it into a list of directions 50 | def parseCode(self, code): 51 | moves = [] 52 | if code[0] == '1': 53 | moves.append('UP') 54 | if code[1] == '1': 55 | moves.append('RIGHT') 56 | if code[2] == '1': 57 | moves.append('DOWN') 58 | if code[3] == '1': 59 | moves.append('LEFT') 60 | return moves 61 | # Returns the tile in direction direction 62 | def getTileInDirection(self, board, direction): 63 | vect = moves.get(direction) 64 | pos = addPositions(self.pos, vect) 65 | return board[pos] 66 | 67 | # Returns all the direction you can take from a tile 68 | def getAccessibleDirections(self, board): 69 | steps = self.getPossibleSteps(board) 70 | return list(map(lambda x: x.direction, steps)) 71 | 72 | # Returns all the accessible tiles from this tile in one move 73 | def getPossibleSteps(self, board): 74 | direc = [] 75 | for move in self.availableMoves: 76 | vect = moves.get(move) 77 | dest = addPositions(self.pos, vect) 78 | 79 | if dest in board and oppositeMove.get(move) in board[dest].availableMoves: 80 | direc.append(Step(move, board[dest])) 81 | 82 | return direc 83 | 84 | # Returns the path from this tile to the tile you want 85 | def findPath(self, board, pos): 86 | currentTile = self 87 | visited = {currentTile} 88 | toVisit = deque() 89 | toVisit.append((currentTile, [])) 90 | while toVisit: 91 | currentTile, path = toVisit.popleft() 92 | if currentTile.pos == pos: 93 | return path 94 | else: 95 | for step in currentTile.getPossibleSteps(board): 96 | if step.tile not in visited: 97 | toVisit.append((step.tile, path + [step.direction])) 98 | visited.add(step.tile) 99 | 100 | 101 | # game loop 102 | while True: 103 | # Here are my local variables, these are reset at each turn 104 | myHeroPos = Vector(0,0) 105 | quests = [] 106 | items = [] 107 | 108 | board = {} # The board is a dictionnary linking the position of a tile to the tile object 109 | 110 | # Here we get the type of turn : 0 for push and 1 for move 111 | turnType = int(input()) 112 | 113 | # Here we create our representation of the board 114 | for y in range(boardHeight): 115 | x = 0 116 | for tile in input().split(): 117 | temp = Tile([x, y], tile) 118 | board[Vector(x, y)] = Tile(Vector(x, y), tile) 119 | x += 1 120 | 121 | # Here we get our position (and ignore all the other infos) 122 | for i in range(2): 123 | numPlayerCards, playerX, playerY, playerTile = input().split() 124 | numPlayerCards = int(numPlayerCards) 125 | playerX = int(playerX) 126 | playerY = int(playerY) 127 | if i == 0: 128 | myHeroPos = Vector(playerX, playerY) 129 | 130 | # Here we get the position of our items (and ignore the ones of the other player) 131 | numItems = int(input()) 132 | for i in range(numItems): 133 | itemName, itemX, itemY, itemPlayerId = input().split() 134 | itemPlayerId = int(itemPlayerId) 135 | if itemPlayerId == 0: 136 | items.append(Item(itemName, Vector(int(itemX), int(itemY)))) 137 | 138 | # Here we get the list of the items we want (and ignore the ones of the other player) 139 | numQuests = int(input()) 140 | for i in range(numQuests): 141 | questItemName, questPlayerId = input().split() 142 | questPlayerId = int(questPlayerId) 143 | if questPlayerId == 0: 144 | quests.append(questItemName) 145 | 146 | myHeroTile = board[myHeroPos] 147 | goalPos = Vector(0,0) 148 | 149 | # Now we detect if we are player one or two (just to avoid having boring draws against ourself) 150 | if player == -1: 151 | if myHeroPos[0] == 0: 152 | player = 0 153 | else: 154 | player = 1 155 | toggle = player # to avoid boring draws we start our push phase differently 156 | 157 | # We select the item we want to search 158 | for item in items: 159 | if item.name == quests[0]: 160 | goalPos = item.position 161 | 162 | # If it's a push turn 163 | if turnType == 0: 164 | # We will alternate between vertical and horizontal push to shuffle the grid 165 | # and change the line or the column we want to push at each push 166 | if toggle: 167 | print("PUSH", column, "RIGHT") 168 | column = (column + 1) % boardHeight 169 | else: 170 | print("PUSH", row, "DOWN") 171 | row = (row + 1) % boardHeight 172 | toggle = not(toggle) 173 | 174 | # If it's a move turn 175 | else: 176 | # default value: we do nothing 177 | action = 'PASS' 178 | 179 | # if we can go somewhere select one of the available directions 180 | if len(myHeroTile.getAccessibleDirections(board)) != 0: 181 | action = "MOVE " + myHeroTile.getAccessibleDirections(board)[moveType % len(myHeroTile.getAccessibleDirections(board))] 182 | 183 | # if we can go to our goal item go for it (but we don't want to be too strong so we don't go for more that 2 tiles at once) 184 | path = myHeroTile.findPath(board,goalPos) 185 | if path: 186 | action = 'MOVE ' + " ".join(path[:2]) 187 | 188 | # output the move we chose 189 | print(action) 190 | 191 | # Change the index of the next direction we'll chose if we can go somewhere but not get item 192 | moveType = (moveType+1) % len(moves) -------------------------------------------------------------------------------- /config/level2/Boss.py3: -------------------------------------------------------------------------------- 1 | import sys 2 | import math 3 | import random 4 | 5 | random.seed('Help the Christmas elves fetch presents in a magical labyrinth!') 6 | 7 | board_width, board_height = [7,7] 8 | 9 | 10 | def approach(target_x, player_x, target_y, player_y): 11 | if target_x < player_x: 12 | return 'LEFT' 13 | elif target_x > player_x: 14 | return 'RIGHT' 15 | elif target_y < player_y: 16 | return 'UP' 17 | else: 18 | return 'DOWN' 19 | 20 | 21 | spinner = { 22 | (-1, -1): 'RIGHT', 23 | (0, -1): 'RIGHT', 24 | (1, -1): 'DOWN', 25 | (1, 0): 'DOWN', 26 | (1, 1): 'LEFT', 27 | (0, 1): 'LEFT', 28 | (-1, 1): 'UP', 29 | (-1, 0): 'UP' 30 | } 31 | moves = { 32 | 'LEFT': [-1,0], 33 | 'UP':[0,-1], 34 | 'RIGHT':[1,0], 35 | 'DOWN':[0,1] 36 | } 37 | oppositeMove = { 38 | 'LEFT': 'RIGHT', 39 | 'UP':'DOWN', 40 | 'RIGHT':'LEFT', 41 | 'DOWN':'UP' 42 | } 43 | def addPositions(pos1,pos2): 44 | return [pos1[0]+pos2[0],pos1[1]+pos2[1]] 45 | class Tile: 46 | def __init__(self, pos, code): 47 | self.pos = pos 48 | self.availableMoves = self.transcribeCode(code) 49 | def transcribeCode(self,code): 50 | tilemoves = [] 51 | if (code[0] == '1'): 52 | tilemoves.append('UP') 53 | if (code[1] == '1'): 54 | tilemoves.append('RIGHT') 55 | if (code[2] == '1'): 56 | tilemoves.append('DOWN') 57 | if (code[3] == '1'): 58 | tilemoves.append('LEFT') 59 | return tilemoves 60 | def getAccessibleDirections(self,board): 61 | direc = [] 62 | for move in self.availableMoves: 63 | vect = moves.get(move) 64 | dest = addPositions(self.pos,vect) 65 | try : 66 | if (oppositeMove.get(move) in board[dest[0],dest[1]].availableMoves): 67 | direc.append(move) 68 | except KeyError: 69 | pass 70 | return direc 71 | # game loop 72 | while True: 73 | target_x = 0 74 | target_y = 0 75 | player_x = 0 76 | player_y = 0 77 | 78 | turn_type = int(input()) 79 | board = {} 80 | for y in range(board_height): 81 | x=0 82 | for tile in input().split(): 83 | temp = Tile([x,y],tile) 84 | board[x,y] = Tile([x,y],tile) 85 | x += 1 86 | for i in range(2): 87 | # num_player_cards: the number of cards in the stack for each player 88 | num_player_cards, x, y, player_tile = input().split() 89 | num_player_cards = int(num_player_cards) 90 | x = int(x) 91 | y = int(y) 92 | if i == 0: 93 | player_x = x 94 | player_y = y 95 | print(x, y, file=sys.stderr) 96 | # the total number of items available on board and on player tiles (does not include quest cards) 97 | items = {} 98 | num_items = int(input()) 99 | for i in range(num_items): 100 | item_name, item_x, item_y, item_player_id = input().split() 101 | # print(item_name, file=sys.stderr) 102 | item_x = int(item_x) 103 | item_y = int(item_y) 104 | item_player_id = int(item_player_id) 105 | items[item_name + str(item_player_id)] = (item_x, item_y) 106 | 107 | # the total number of available quest cards for both players 108 | num_quests = int(input()) 109 | for i in range(num_quests): 110 | quest_item_name, quest_player_id = input().split() 111 | quest_player_id = int(quest_player_id) 112 | if quest_player_id == 0: 113 | target_x = items[quest_item_name + str(quest_player_id)][0] 114 | target_y = items[quest_item_name + str(quest_player_id)][1] 115 | 116 | if turn_type == 0: 117 | if target_x < 0: 118 | direction = random.choice(('RIGHT', 'UP', 'LEFT', 'DOWN')) 119 | to_push = random.randint(0, 6) 120 | print(f'PUSH {to_push} {direction}') 121 | else: 122 | diff = (target_x - player_x, target_y - player_y) 123 | if diff in spinner: 124 | direction = spinner[diff] 125 | to_push = target_y if (direction in ( 126 | 'RIGHT', 'LEFT')) else target_x 127 | print(f'PUSH {to_push} {direction}') 128 | else: 129 | if abs(diff[0]) != 1 and player_y != target_y: 130 | direction = 'LEFT' if player_x < target_x else 'RIGHT' 131 | to_push = target_y 132 | print(f'PUSH {to_push} {direction}') 133 | elif abs(diff[1]) != 1 and player_x != target_x: 134 | direction = 'UP' if player_y < target_y else 'DOWN' 135 | to_push = target_x 136 | print(f'PUSH {to_push} {direction}') 137 | else: 138 | direction = random.choice(('RIGHT', 'UP', 'LEFT', 'DOWN')) 139 | to_push = random.randint(0, 6) 140 | print(f'PUSH {to_push} {direction}') 141 | else: 142 | tcheby = max(abs(target_x - player_x), abs(target_y - player_y)) 143 | manhat = abs(target_x - player_x) + abs(target_y - player_y) 144 | currTile = board[player_x,player_y] 145 | nextMove = approach(target_x, player_x, target_y, player_y) 146 | if (tcheby != 1 or manhat == 1) and nextMove in currTile.getAccessibleDirections(board): 147 | print("MOVE " + approach(target_x, player_x, target_y, player_y)) 148 | else: 149 | print("PASS") 150 | -------------------------------------------------------------------------------- /config/level2/welcome_en.html: -------------------------------------------------------------------------------- 1 |
2 |

You've made it to the next league

3 |
You will have to complete 6 quests while having 1 revealed!
4 |
See the updated statement for details.
5 |
6 | -------------------------------------------------------------------------------- /config/level2/welcome_fr.html: -------------------------------------------------------------------------------- 1 |
2 |

Vous avez atteint la ligue Bois 1

3 |
Vous devrez terminer 6 quêtes en connaissant 1 quête à la fois !
4 |
Pour en savoir plus, allez voir l'énoncé.
5 |
6 | -------------------------------------------------------------------------------- /config/level3/welcome_en.html: -------------------------------------------------------------------------------- 1 |
2 |

You've made it to the next league

3 |
You will have to complete 12 quests while having 3 revealed!
4 |
See the updated statement for details.
5 |
6 | -------------------------------------------------------------------------------- /config/level3/welcome_fr.html: -------------------------------------------------------------------------------- 1 |
2 |

Vous avez atteint la ligue Bronze.

3 |
Vous devrez terminer 12 quêtes en connaissant au maximum 3 quête à la fois !
4 |
Pour en savoir plus, allez voir l'énoncé.
5 |
6 | -------------------------------------------------------------------------------- /config/statement_en.html.tpl: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
11 |
12 | 13 |
14 |

15 | 16 | This is a league based challenge. 17 | 18 | 19 | Welcome to the Wood1 league! 20 | 21 | 22 | Welcome to the Bronze league! 23 | 24 |

25 | 26 | 27 | Wood leagues should be considered as a tutorial which lets players discover the different rules of the game.
28 | In Bronze league, all rules will be unlocked and the real challenge will begin. 29 | 30 | 31 | In Wood 1, players must complete 6 quests. At most 1 quest is revealed. 32 | 33 | 34 | In Bronze, players must complete 12 quests. At most 3 quests are revealed. 35 | 36 |
37 |
38 | 39 | 40 | 41 |
42 |

43 |   44 | The Goal 45 |

46 |
47 | Make your way to the items on the board and be the first to complete your quests! 48 |
49 |
50 | 51 | 52 |
53 |

54 |   55 | Rules 56 |

57 |
58 |

59 | The game is played by 2 players on a 7x7 board with 49 square tiles. The (0,0) coordinate corresponds to the top 60 | left corner. 61 |

62 |

63 | Each player has 1 tile placed outside the board which they can use to push a row or a column on the board, 64 | trying to make a path toward their quest items. When a user pushes a tile into the board, the tile that gets 65 | pushed out will become the new player's tile. 66 |

67 |

68 | The board 69 |

70 |
    71 |
  • 72 | The board contains square tiles with paths on them. A path can lead to one of the four directions 73 | (UP, RIGHT, DOWN and LEFT). 74 |
  • 75 |
  • 76 | Some tiles have an item on them. 77 |
  • 78 |
79 |

80 | Quest 81 |

82 |
    83 |
  • 84 | Each quest corresponds to an item on the board. Quests are the same for both players, but each item is unique in the deck. 85 |
  • 86 |
  • 87 | To complete a quest, a player must move to the tile containing the corresponding item. The quest must be 88 | revealed to be able to complete it. 89 |
  • 90 | 91 |
  • 92 | For this league, each player has 1 quest to complete. 93 |
  • 94 | 95 | 96 |
  • 99 | For this league, each player has 6 quests to complete. At most 1 quest is 100 | revealed. 101 |
  • 102 |
  • 105 | When an item is collected, it is removed from the tile and the quest is marked as completed and removed 106 | from the player's quest deck. After the turn ends, a new quest is revealed (if available). 107 |
  • 108 | 109 | 110 |
  • 113 | For this league, each player has 12 quests to complete. At most 3 quests are 114 | revealed. They can be collected in any order. 115 |
  • 116 |
  • 119 | A player can complete multiple quests in one turn. 120 |
  • 121 | 122 | 123 |
  • 124 | For this league, each player has 12 quests to complete. At most 3 quests are 125 | revealed. They can be collected in any order. 126 |
  • 127 |
  • 128 | A player can complete multiple quests in one turn. 129 |
  • 130 | 131 | 132 |
  • 133 | When an item is collected, it is removed from the tile and the quest is marked as completed and removed 134 | from the player's quest deck. After the turn ends, a new quest is revealed (if available). 135 |
  • 136 | 137 | 138 | 139 |
140 |

141 | The game turns 142 |

143 |

144 | Each game turn alternates between a PUSH turn and a MOVE turn. 145 | The first turn is always a PUSH turn. 146 |

147 |

148 | Rules for pushing 149 |

150 |
    151 |
  • 152 | Each player can choose to push any row or column on the board. Rows can only be pushed horizontally 153 | (LEFT or RIGHT), while columns can only be pushed vertically 154 | (UP or DOWN). 155 |
  • 156 |
  • 157 | If both players push the same row or column, no matter the direction, nothing happens. 158 |
  • 159 |
  • 160 | If push commands intersect (one is horizontal and the other one vertical), the row is pushed first, 161 | followed by the column. Otherwise, they get pushed simultaneously. 162 |
  • 163 |
  • 164 | If a player is on a tile which gets pushed out of the map, the player is wrapped on the other end of the line. 165 |
  • 166 |
167 |

168 | Rules for moving 169 |

170 |
    171 |
  • 172 | To allow a player to move between two adjacent tiles, the tiles' respective paths must connect to form a longer 173 | path. Moving to an adjacent tile counts as 1 step. 174 |
  • 175 |
  • 176 | Each player can move at most 20 steps during this turn via connected paths. 177 |
  • 178 |
179 |

180 | Actions 181 |

182 | Every PUSH turn the player must: 183 |
    184 |
  • 185 | PUSH id direction: to push a row id 186 | (direction LEFT or RIGHT) or a column id 187 | (direction UP or DOWN). 188 |
  • 189 |
190 | Every MOVE turn the player must either: 191 |
    192 |
  • 193 | MOVE direction: to move one step towards direction 194 | LEFT, RIGHT, UP or DOWN. 195 |
  • 196 |
  • 197 | PASS: to do nothing. 198 |
  • 199 |
200 |

201 | A MOVE can contain up to 20 directions, each direction separated by a 202 | space  . 203 |

204 |
205 | Note: You may toggle tile scenery on/off in the settings panel (). 206 |
207 |
208 | 209 | 210 |
211 |
212 |
213 |
Victory Conditions
214 |
215 |
    216 |
  • 217 | You complete all your quests before your opponent (if both players complete their quests in the same turn, the 218 | game will end as a draw). 219 |
  • 220 |
  • 221 | After 150 turns, you complete more quests than your opponent. 222 |
  • 223 |
224 |
225 |
226 |
227 | 228 | 229 |
230 |
231 |
232 |
Loss Conditions
233 |
234 |
    235 |
  • 236 | Your program times out. 237 |
  • 238 |
  • 239 | Your program provides invalid output for the active turn type. 240 |
  • 241 |
  • 242 | You complete fewer quests than your opponent. 243 |
  • 244 |
245 |
246 |
247 |
248 |
249 | 250 | 251 |
252 |

253 |   254 | Advanced Details 255 |

256 |
257 |

258 | You can see the game's source code here: https://github.com/CodinGameCommunity/XmasRush. 259 |

260 |

261 |

    262 |
  • 263 | Players don't need to finish their turn on an item to collect it. Moving over it during a longer movement 264 | sequence is sufficient to complete revealed quests. 265 |
  • 266 |
  • 267 | An invalid move ends the current movement. Moving to a direction without a connected path in that direction 268 | is considered as invalid. 269 |
  • 270 |
  • 271 | It is possible to complete a quest during a push turn. If a push command warps a player onto a quest item, 272 | the quest, if revealed, is completed and another one is revealed at the end of the turn. 273 |
  • 274 |
  • 275 | If no modification of the labyrinth happens for 10 successive turns of modification, the game ends, independently of both players' movement actions. 276 |
  • 277 |
278 |

279 |
280 |
281 | 282 |
283 |

284 |   285 | Game Input 286 |

287 | 288 | 289 |
290 |
Input for one game turn
291 |
292 | First line: Integer turnType: the game turn type: 293 |
    294 |
  • 295 | 0: a PUSH turn. 296 |
  • 297 |
  • 298 | 1: a MOVE turn. 299 |
  • 300 |
301 | Next 7 lines: 7 space-separated strings 302 | representing each tile on a row, starting from the top. Each tile is represented by a 4 digit group, each digit 303 | corresponding to a directional path: up, right, down, left. 1 means the tile has a path for the 304 | respective direction, 0 means the tile doesn't.
305 | Next 2 lines: for each player, numPlayerCards, 306 | playerX, playerY, playerTile: 307 |
    308 |
  • 309 | Integer numPlayerCards: the total number of quests for a player (hidden and revealed). 310 |
  • 311 |
  • 312 | Integer playerX: the player's x position on the board (the column). 313 |
  • 314 |
  • 315 | Integer playerY: the player's y position on the board (the row). 316 |
  • 317 |
  • 318 | String playerTile: the player's tile in 4 digit format. 319 |
  • 320 |
321 | Note: The player's input always comes first, the opponent's input comes second.
322 | Next line: Integer numItems: the total number of items 323 | available on board and on player tiles.
324 | Next numItems lines: itemName, itemX, 325 | itemY, itemPlayerId: 326 |
    327 |
  • 328 | String itemName: the item's name. 329 |
  • 330 |
  • 331 | Integer itemX: the item's x position on the board (the column). 332 |
  • 333 |
  • 334 | Integer itemY: the item's y position on the board (the row). 335 |
  • 336 |
  • 337 | Integer itemPlayerId: the id of the player who can collect the item. 338 |
  • 339 |
340 | Note: If an item is on a player's tile, itemX and itemY will both be 341 | -1 for the player and -2 for the opponent.
342 | Next line: Integer numQuests: the total number of revealed quests 343 | for both players.
344 | Next numQuests lines: questItemName, 345 | questPlayerId: 346 |
    347 |
  • 348 | String questItemName: the item's name. 349 |
  • 350 |
  • 351 | Integer questPlayerId: the id of the player the quest belongs to. 352 |
  • 353 |
354 | Note: The player's id is always 0 and the opponent's 1. 355 |
356 |
357 | 358 | 359 |
360 |
Output for one PUSH game turn
361 |
362 |
    363 |
  • 364 | PUSH id direction where id is between 0 365 | and 6, and direction can be UP, DOWN, 366 | LEFT or RIGHT. 367 |
  • 368 |
369 | Example: PUSH 3 UP will push the fourth column upwards. 370 |
371 |
Output for one MOVE game turn
372 |
373 |
    374 |
  • 375 | MOVE direction where direction can be UP, 376 | DOWN, LEFT or RIGHT. 377 |
  • 378 |
  • 379 | PASS to skip moving this turn. 380 |
  • 381 |
382 | A MOVE can contain up to 20 directions, each direction separated by a 383 | space  .
384 | Example: MOVE LEFT UP RIGHT will make the player move left, then up, then right. 385 |
386 |
387 | 388 | 389 |
390 |
Constraints
391 |
392 | board width = 7
393 | board height = 7
394 | 395 | numPlayerCards = 1
396 | 0numItems2
397 | 0numQuests2
398 | 399 | 400 |
403 | 0numPlayerCards6
404 | 0numItems12
405 | 0numQuests2
406 |
407 | 408 | 409 |
412 | 0numPlayerCards12
413 | 0numItems24
414 | 0numQuests6
415 |
416 | 417 | 418 | 0numPlayerCards12
419 | 0numItems24
420 | 0numQuests6
421 | 422 | 423 |
424 | Response time for the first turn ≤ 1s
425 | Response time per turn ≤ 50ms
426 |
427 |
428 |
429 | 430 |
435 |
436 | 437 |
438 |

439 | What is in store in the higher leagues? 440 |

441 |

442 | The extra rules available in higher leagues are: 443 |

451 |

452 |
453 | 454 | 455 |
456 | -------------------------------------------------------------------------------- /config/statement_fr.html.tpl: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
11 |
12 | 13 |
14 |

15 | 16 | Ce puzzle se déroule en ligues. 17 | 18 | 19 | Bienvenue en ligue Bois 1 ! 20 | 21 | 22 | Bienvenue en ligue Bronze ! 23 | 24 |

25 | 26 | 27 | Les ligues Bois doivent être considérées comme un tutoriel pour apprendre les différentes règles du jeu.
28 | En ligue Bronze, toutes les règles sont débloquées et alors débute le challenge, le vrai. 29 | 30 | 31 | En ligue Bois 1, chaque joueur doit terminer 6 quêtes. 1 quête au maximum est révélée. 32 | 33 | 34 | En ligue Bronze, chaque joueur doit terminer 12 quêtes. 3 quêtes au maximum sont révélées. 35 | 36 |
37 |
38 | 39 | 40 | 41 |
42 |

43 |   44 | Objectif 45 |

46 |
47 | Faites-vous un chemin vers les objets disséminés dans le labyrinthe et soyez le premier à terminer vos quêtes ! 48 |
49 |
50 | 51 | 52 |
53 |

54 |   55 | Règles du jeu 56 |

57 |
58 |

59 | Une partie est jouée par 2 joueurs sur un plateau 7x7 de 49 tuiles carrées. Les coordonnées (0,0) correspondent à la tuile dans le coin en haut à gauche. 60 |

61 |

62 | Chaque joueur possède une tuile qu'il utilise pour pousser une ligne ou une colonne du plateau, de façon à créer un chemin vers les objets de leurs quêtes. 63 | Quand un joueur utilise sa tuile pour déplacer une colonne ou une ligne, il récupère la tuile qui est poussée hors du plateau. 64 |

65 |

66 | Le plateau de jeu 67 |

68 |
    69 |
  • 70 | Chaque tuile contient des chemins. Un chemin peut mener à 1 des 4 directions possibles : 71 | UP (vers le haut), RIGHT (vers la droite), DOWN (vers le bas) et LEFT(vers la gauche). 72 |
  • 73 |
  • 74 | Certaines tuiles contiennent des objets. 75 |
  • 76 |
77 |

78 | Les quêtes 79 |

80 |
    81 |
  • 82 | Chaque quête correspond à un objet sur le plateau. 83 |
  • 84 |
  • 85 | Pour terminer une quête, un joueur doit se déplacer sur la tuile contenant l'objet correspondant. La quête doit être révélée pour qu'un joueur puisse la terminer. 86 |
  • 87 | 88 |
  • 89 | Dans cette ligue, chaque joueur n'a qu' 1 quête à terminer. 90 |
  • 91 | 92 | 93 |
  • 96 | Dans cette ligue, chaque joueur a 6 quêtes à terminer. 1 quête au maximum est révélée. 97 |
  • 98 |
  • 101 | Quand un objet est récupéré par un joueur, l'objet est retiré du jeu et la quête est terminée. 102 | Après la fin du tour, une nouvelle quête est révélée (si disponible). 103 |
  • 104 | 105 | 106 |
  • 109 | Dans cette ligue, chaque joueur a 12 quêtes à terminer. 3 quête au maximum est révélée. Elles peuvent êtres terminées dans n'importe quel ordre. 110 |
  • 111 |
  • 114 | Un joueur peut terminer plusieurs quêtes dans un même tour. 115 |
  • 116 | 117 | 118 |
  • 119 | Dans cette ligue, chaque joueur a 12 quêtes à terminer. 3 quête au maximum est révélée. Elles peuvent êtres terminées dans n'importe quel ordre. 120 |
  • 121 |
  • 122 | Un joueur peut terminer plusieurs quêtes dans un même tour. 123 |
  • 124 | 125 | 126 |
  • 127 | Quand un objet est récupéré par un joueur, l'objet est retiré du jeu et la quête est terminée. 128 | Après la fin du tour, une nouvelle quête est révélée (si disponible). 129 |
  • 130 | 131 | 132 | 133 |
134 |

135 | Les différents tours de jeu 136 |

137 |

138 | Il y a deux types de tours qui s'alternent l'un après l'autre : un tour de modification (PUSH) et un tour de déplacement (MOVE). 139 | Le premier tour de jeu est toujours un tour de modification (PUSH). 140 |

141 |

142 | Règles pour modifier le labyrinthe 143 |

144 |
    145 |
  • 146 | Chaque joueur peut choisir de pousser n'importe quelle ligne ou colonne du plateau. Les lignes sont poussées horizontalement 147 | (LEFT ou RIGHT), tandis que les colonnes sont poussées verticalement 148 | (UP ou DOWN). 149 |
  • 150 |
  • 151 | Si les deux joueurs choisissent de pousser la même ligne ou la même colonne, rien ne se passe. 152 |
  • 153 |
  • 154 | Si deux actions de modification s'intersectent (l'une horizontale et l'autre verticale) la ligne est poussée en premier, suivie par la colonne. 155 | Dans le cas contraire, les deux lignes ou deux colonnes sont poussées simultanément. 156 |
  • 157 |
  • 158 | Si un joueur est poussé hors du plateau de jeu, il se retrouve sur la tuile qui a servi à pousser la ligne ou la colonne. 159 |
  • 160 |
161 |

162 | Règles pour se déplacer 163 |

164 |
    165 |
  • 166 | Pour qu'un joueur puisse se déplacer entre deux tuiles, les chemins des deux tuiles doivent se connecter pour en former un plus long. 167 | Se déplacer sur une tuile adjacente compte pour 1 pas. 168 |
  • 169 |
  • 170 | Chaque joueur peut se déplacer de 20 pas au maximum par tour. 171 |
  • 172 |
173 |

174 | Actions possibles 175 |

176 | Chaque tour de PUSH, le joueur doit : 177 |
    178 |
  • 179 | PUSH id direction: pour modifier une ligne id 180 | (direction LEFT ou RIGHT) ou une colonne id 181 | (direction UP ou DOWN). 182 |
  • 183 |
184 | Chaque tour de MOVE, le joueur doit : 185 |
    186 |
  • 187 | MOVE direction: pour se déplacer vers la direction 188 | LEFT, RIGHT, UP ou DOWN. 189 |
  • 190 |
  • 191 | PASS: pour ne rien faire et passer son tour. 192 |
  • 193 |
194 |

195 | Une action MOVE doit contenir jusqu'à 20 directions, chaque direction séparée par un 196 | space  . 197 |

198 |
199 | Note : Vous pouvez activer ou désactiver le décor tile dans les options de la vidéo (). 200 |
201 |
202 | 203 | 204 |
205 |
206 |
207 |
Conditions de victoire
208 |
209 |
    210 |
  • 211 | Vous terminez toutes vos quêtes avant votre adversaire. Si les deux joueurs terminent leur quêtes au même tour de jeu, la partie se termine en match nul. 212 |
  • 213 |
  • 214 | Après 150 tours, vous terminez plus de quêtes que votre adversaire. 215 |
  • 216 |
217 |
218 |
219 |
220 | 221 | 222 |
223 |
224 |
225 |
Conditions de défaite
226 |
227 |
    228 |
  • 229 | Votre programme ne répond pas dans le temps imparti. 230 |
  • 231 |
  • 232 | Votre programme répond avec une sortie invalide pour le type de tour. 233 |
  • 234 |
  • 235 | Vous terminez moins de quêtes que votre adversaire. 236 |
  • 237 |
238 |
239 |
240 |
241 |
242 | 243 | 244 |
245 |

246 |   247 | Détails de règles 248 |

249 |
250 |

251 | Vous pouvez retrouver le code source du jeu ici : https://github.com/CodinGameCommunity/XmasRush. 252 |

253 |

254 |

    255 |
  • 256 | Les joueurs n'ont pas besoin de terminer leur tour sur un objet pour le récupérer. 257 | Se déplacer sur une tuile avec un objet pendant une séquence de mouvement est suffisant pour terminer une quête révélée. 258 |
  • 259 |
  • 260 | Une action de déplacement invalide termine le déplacement en cours. Est considérée comme invalide, une commande de déplacement vers une direction sans connection de chemins. 261 |
  • 262 |
  • 263 | Il est possible de terminer une quête pendant un tour de modification. Si la commande déplace le joueur hors du jeu puis sur la tuile utilisée qui contient l'objet d'une quête révélée, alors cette quête est terminée et une nouvelle est révélée à la fin du tour. 264 |
  • 265 |
  • 266 | L'ordre des quêtes est le même pour les deux joueurs. Tous les objets d'un joueur sont uniques. 267 |
  • 268 |
  • 269 | Si le labyrinthe n'est pas modifié pendant 10 tours consécutifs de modification, la partie se termine, indépendamment des actions de mouvement des joueurs. 270 |
  • 271 |
272 |

273 |
274 |
275 | 276 |
277 |

278 |   279 | Protocole du jeu 280 |

281 | 282 | 283 |
284 |
Entrée pour un tour de jeu
285 |
286 | Première ligne: un entier turnType pour le type de tour de jeu 287 |
    288 |
  • 289 | 0: un tour de modification (PUSH). 290 |
  • 291 |
  • 292 | 1: un tour de déplacement (MOVE). 293 |
  • 294 |
295 | Les 7 lignes suivantes: 7 chaînes de caractères 296 | representant chaque tuile d'une ligne each, en démarrant à partir du haut. Chaque tuile est représentée par 4 chiffres, chaque chiffre représentant une direction : 297 | en haut, à droite, en bas, à gauche. 1 signifie que la tuile contient un chemin vers cette direction, 0 qu'elle n'en contient pas.
298 | Les 2 lignes suivantes: pour chaque joueur, numPlayerCards, 299 | playerX, playerY, playerTile: 300 |
    301 |
  • 302 | Un entier numPlayerCards: le nombre total de quêtes non terminées d'un joueur (cachée ou révélée). 303 |
  • 304 |
  • 305 | Un entier playerX: la position x du joueur sur le plateau (sa colonne). 306 |
  • 307 |
  • 308 | Un entier playerY: la position y du joueur sur le plateau (sa ligne). 309 |
  • 310 |
  • 311 | Une chaîne de caractères playerTile: la tuile du joueur dans le format à 4 chiffres. 312 |
  • 313 |
314 | Note: L'entrée du joueur est toujours donnée en premier, celle de son adversaire en second.
315 | Ligne suivante: un entier numItems pour le nombre total d'objets disponibles sur le plateau et sur les tuiles des joueurs.
316 | Les numItems lignes suivantes: itemName, itemX, 317 | itemY, itemPlayerId: 318 |
    319 |
  • 320 | Une chaîne de caractères itemName: le nom de l'objet. 321 |
  • 322 |
  • 323 | Un entier itemX: la position x de l'objet sur le plateau (sa colonne). 324 |
  • 325 |
  • 326 | Un entier itemY: la position y de l'objet sur le plateau (sa ligne). 327 |
  • 328 |
  • 329 | Un entier itemPlayerId: l'identifiant du joueur à qui l'objet appartient. 330 |
  • 331 |
332 | Note: Si un objet se trouve sur la tuile d'un joueur, itemX et itemY serons égaux à 333 | -1 pour ce joueur, et -2 pour son adversaire.
334 | Ligne suivante: un entier numQuests pour le nombre total de quêtes révélées pour les deux joueurs.
335 | Les numQuests lignes suivantes: questItemName, 336 | questPlayerId: 337 |
    338 |
  • 339 | Une chaîne de caractères questItemName: le nom de l'objet associé à la quête. 340 |
  • 341 |
  • 342 | Un entier questPlayerId: l'idientifiant du joueur qui possède cette quête. 343 |
  • 344 |
345 | Note: l'identifiant du joueur est toujours 0 et celui de son adversaire 1. 346 |
347 |
348 | 349 | 350 |
351 |
Sortie pour un tour de modification
352 |
353 |
    354 |
  • 355 | PUSH id directionid est compris entre 0 356 | et 6, et où direction vaut UP, DOWN, 357 | LEFT ou RIGHT. 358 |
  • 359 |
360 | Exemple: PUSH 3 UP poussera la quatrième colonne vers le haut. 361 |
362 |
Sortie pour un tour de déplacement
363 |
364 |
    365 |
  • 366 | MOVE directiondirection vaut UP, 367 | DOWN, LEFT ou RIGHT. 368 |
  • 369 |
  • 370 | PASS pour passer ton tour. 371 |
  • 372 |
373 | Une action de déplacement (MOVE) peut inclure jusqu'à 20 directions, les directions étant séparées par un 374 | espace  .
375 | Exemple: MOVE LEFT UP RIGHT déplacera le joueur à gauche, puis vers le haut, puis vers la droite. 376 |
377 |
378 | 379 | 380 |
381 |
Contraintes
382 |
383 | largeur du plateau = 7
384 | hauteur du plateau = 7
385 | 386 | numPlayerCards = 1
387 | 0numItems2
388 | 0numQuests2
389 | 390 | 391 |
394 | 0numPlayerCards6
395 | 0numItems12
396 | 0numQuests2
397 |
398 | 399 | 400 |
403 | 0numPlayerCards12
404 | 0numItems24
405 | 0numQuests6
406 |
407 | 408 | 409 | 0numPlayerCards12
410 | 0numItems24
411 | 0numQuests6
412 | 413 | 414 |
415 | Temps de réponse pour le premier tour ≤ 1s
416 | Temps de réponse pour un tour de jeu ≤ 50ms
417 |
418 |
419 |
420 | 421 |
426 |
427 | 428 |
429 |

430 | Qu'est-ce qui vous attend dans les ligues supérieures ? 431 |

432 |

433 | Voici les règles supplémentaires à débloquer dans les ligues supérieures : 434 |

442 |

443 |
444 | 445 | 446 |
447 | -------------------------------------------------------------------------------- /config/stub.txt: -------------------------------------------------------------------------------- 1 | gameloop 2 | read turnType:int 3 | loop 7 4 | loopline 7 tile:word(4) 5 | loop 2 6 | read numPlayerCards:int playerX:int playerY:int playerTile:word(4) 7 | read numItems:int 8 | loop numItems 9 | read itemName:word(10) itemX:int itemY:int itemPlayerId:int 10 | read numQuests:int 11 | loop numQuests 12 | read questItemName:word(10) questPlayerId:int 13 | write PUSH 3 RIGHT 14 | 15 | INPUT 16 | numPlayerCards: the total number of quests for a player (hidden and revealed) 17 | numItems: the total number of items available on board and on player tiles 18 | numQuests: the total number of revealed quests for both players 19 | 20 | STATEMENT 21 | Help the Christmas elves fetch presents in a magical labyrinth! 22 | 23 | OUTPUT 24 | PUSH | MOVE | PASS 25 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.codingame.game 6 | Xmas-Rush 7 | 1.0-SNAPSHOT 8 | 9 | 10 | 2.15 11 | 1.8 12 | 1.8 13 | 14 | 15 | 16 | 17 | com.codingame.gameengine 18 | core 19 | ${gamengine.version} 20 | 21 | 22 | 23 | com.codingame.gameengine 24 | module-entities 25 | ${gamengine.version} 26 | 27 | 28 | 29 | com.codingame.gameengine 30 | runner 31 | ${gamengine.version} 32 | 33 | 34 | 35 | junit 36 | junit 37 | 4.12 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/InputActions/Action.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.InputActions; 2 | 3 | public abstract class Action { 4 | private final Type type; 5 | 6 | public Action(Type type) { 7 | this.type = type; 8 | } 9 | 10 | //The type of an input action. 11 | public enum Type { 12 | PUSH(0), 13 | MOVE(1), 14 | PASS(2); 15 | 16 | private int value; 17 | 18 | Type(int value) { 19 | this.value = value; 20 | } 21 | 22 | public int getValue() { 23 | return value; 24 | } 25 | } 26 | 27 | public Type getType() { 28 | return type; 29 | } 30 | 31 | public boolean isLegalAction(Action.Type turnType) { 32 | return turnType.equals(type) || 33 | turnType.equals(Type.MOVE) && type.equals(Type.PASS); 34 | } 35 | 36 | public boolean isPassAction(){ 37 | return type.equals(Type.PASS); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/InputActions/InvalidAction.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.InputActions; 2 | 3 | /** 4 | * Invalid action input exception 5 | */ 6 | public class InvalidAction extends Exception { 7 | /** 8 | * The fatal value says if a player should be disqualified then the exception happens. 9 | */ 10 | private boolean isFatal; 11 | 12 | /** 13 | * Creates an invalid action exception that is fatal by default. 14 | * @param message The exception message. 15 | */ 16 | public InvalidAction(String message) { 17 | super(message); 18 | this.isFatal = true; 19 | } 20 | 21 | /** 22 | * Creates an invalid action exception with fatal information. 23 | * @param message The exception message. 24 | * @param isFatal Says if the exception is fatal and the player should be disqualified. 25 | */ 26 | public InvalidAction(String message, boolean isFatal) { 27 | super(message); 28 | this.isFatal = isFatal; 29 | } 30 | 31 | /** 32 | * @return true if the exception is fatal and the player should be disqualified. 33 | * false otherwise. 34 | */ 35 | public boolean isFatal() { 36 | return isFatal; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/InputActions/MoveAction.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.InputActions; 2 | 3 | import com.codingame.game.Utils.Constants; 4 | 5 | import java.util.ArrayDeque; 6 | import java.util.Deque; 7 | 8 | public class MoveAction extends Action { 9 | private Deque steps; 10 | 11 | public MoveAction(Action.Type type) { 12 | super(type); 13 | this.steps = new ArrayDeque<>(Constants.MAX_MOVE_STEPS); 14 | } 15 | 16 | public void addStep(Constants.Direction direction) { 17 | steps.add(direction); 18 | } 19 | 20 | public Constants.Direction getStep() { 21 | return steps.pollFirst(); 22 | } 23 | 24 | public void setEmpty() { 25 | steps.clear(); 26 | } 27 | 28 | public boolean isEmpty() { 29 | return steps.isEmpty(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/InputActions/PassAction.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.InputActions; 2 | 3 | public class PassAction extends Action { 4 | 5 | public PassAction(Action.Type type) { 6 | super(type); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/InputActions/PushAction.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.InputActions; 2 | 3 | import com.codingame.game.Utils.Constants; 4 | 5 | import java.util.List; 6 | 7 | public class PushAction extends Action { 8 | private int line; 9 | private Constants.Direction direction; 10 | 11 | public PushAction(int line, Constants.Direction direction, Action.Type type) { 12 | super(type); 13 | assert line >= 0 && line < Constants.MAP_SIZE; 14 | this.line = line; 15 | this.direction = direction; 16 | } 17 | 18 | public int getLine() { 19 | return line; 20 | } 21 | 22 | public Constants.Direction getDirection() { 23 | return direction; 24 | } 25 | 26 | public boolean isHorizontal() { 27 | return direction == Constants.Direction.RIGHT || 28 | direction == Constants.Direction.LEFT; 29 | } 30 | 31 | //assumes actions is a list of size two 32 | //and both actions are either horizontal or vertical 33 | public static boolean pushSameLine(List actions) { 34 | assert actions.size() == 2; 35 | assert (actions.get(0).isHorizontal() && actions.get(1).isHorizontal()) || 36 | (!actions.get(0).isHorizontal() && !actions.get(1).isHorizontal()); 37 | return actions.get(0).line == actions.get(1).line; 38 | } 39 | 40 | //used for arrow updates 41 | public String toString() { 42 | return line + "" + direction.asValue(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/AbstractModel.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import java.util.Observable; 4 | 5 | import com.codingame.game.Utils.Vector2; 6 | 7 | public abstract class AbstractModel extends Observable { 8 | private Vector2 pos; 9 | 10 | public AbstractModel(Vector2 pos) { 11 | this.pos = new Vector2(pos); 12 | } 13 | 14 | public void setPos(Vector2 pos) { 15 | this.pos = new Vector2(pos); 16 | } 17 | 18 | public Vector2 getPos() { 19 | return new Vector2(pos); 20 | } 21 | 22 | public void updateState(Object update) { 23 | this.setChanged(); 24 | this.notifyObservers(update); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/CardModel.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import com.codingame.game.Model.StateUpdates.CardPositionUpdate; 4 | import com.codingame.game.Model.StateUpdates.FlipCardUpdate; 5 | import com.codingame.game.Model.StateUpdates.RemoveCardUpdate; 6 | import com.codingame.game.Utils.Constants; 7 | import com.codingame.game.Utils.Vector2; 8 | 9 | public class CardModel extends MovingModel { 10 | private final Item item; 11 | public int cardLayer = -1; 12 | 13 | private void checkRep() { 14 | assert getPos().getX() >= 0 && getPos().getX() < Constants.SCREEN_WIDTH; 15 | assert getPos().getY() >= 0 && getPos().getY() < Constants.SCREEN_HEIGHT; 16 | } 17 | 18 | public CardModel(Item item, Vector2 pos) { 19 | super(pos); 20 | this.item = item; 21 | checkRep(); 22 | } 23 | 24 | public Item getItem() { 25 | return item; 26 | } 27 | 28 | public void flip() { 29 | updateState(new FlipCardUpdate()); 30 | item.setHighlight(); 31 | } 32 | 33 | public void updatePosition() { 34 | updateState(new CardPositionUpdate()); 35 | } 36 | 37 | public void setCardLayer(int layer) { 38 | cardLayer = layer; 39 | } 40 | 41 | public void remove() { 42 | updateState(new RemoveCardUpdate()); 43 | } 44 | 45 | public String cardToString() { 46 | return item.itemToString(); 47 | } 48 | 49 | public String opponentCardToString() { 50 | return item.opponentItemToString(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/GameBoard.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import com.codingame.game.Player; 4 | import com.codingame.game.Utils.Constants; 5 | import com.codingame.game.Utils.Vector2; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class GameBoard { 13 | private final Vector2 centerTilePos = new Vector2(Constants.MAP_WIDTH / 2, Constants.MAP_HEIGHT / 2); 14 | private final String[] centerTilePatterns = new String[]{"1111", "1010", "0101"}; 15 | private final Vector2 playerBasePos = Constants.PLAYER_POSITIONS.get(Constants.PLAYER_INDEX); 16 | private final String playerBasePattern = "0110"; 17 | 18 | private TileModel[][] tiles = new TileModel[Constants.MAP_WIDTH][Constants.MAP_HEIGHT]; 19 | private List tilesWithItems = new ArrayList<>(); 20 | 21 | private List availablePatterns; 22 | 23 | public GameBoard(List availablePatterns) { 24 | this.availablePatterns = availablePatterns; 25 | String centerTilePattern = centerTilePatterns[Constants.random.nextInt(centerTilePatterns.length)]; 26 | setTile(centerTilePos, new TileModel(centerTilePattern, centerTilePos)); 27 | setTile(playerBasePos, new TileModel(playerBasePattern, playerBasePos)); 28 | 29 | fillEmptyTiles(); 30 | } 31 | 32 | //GameBoard generation methods 33 | private void fillEmptyTiles() { 34 | // set up tiles above the secondary diagonal 35 | for (int y = 0; y < Constants.MAP_HEIGHT; y++) { 36 | for (int x = 0; x < Constants.MAP_WIDTH - y; x++) { 37 | Vector2 pos = new Vector2(x, y); 38 | if (getTile(pos) == null) { 39 | String pattern = getRandomAvailablePattern(); 40 | TileModel tile = new TileModel(pattern, pos); 41 | int rotTimes = Constants.random.nextInt(3); 42 | tile.rotate(rotTimes); 43 | setTile(x, y, tile); 44 | } 45 | Vector2 oppositePos = getOppositeTilePos(pos); 46 | TileModel oppositeTile = new TileModel(getTile(pos).getPattern(), oppositePos); 47 | oppositeTile.rotate(2); // rotate 180 deg to be symmetric 48 | setTile(oppositePos, oppositeTile); 49 | } 50 | } 51 | } 52 | 53 | private String getRandomAvailablePattern() { 54 | int index = Constants.random.nextInt(availablePatterns.size()); 55 | String pattern = availablePatterns.get(index); 56 | availablePatterns.remove(index); 57 | return pattern; 58 | } 59 | 60 | private Vector2 getOppositeTilePos(Vector2 pos) { 61 | return new Vector2(Constants.MAP_WIDTH - pos.getX() - 1, 62 | Constants.MAP_HEIGHT - pos.getY() - 1); 63 | } 64 | 65 | //Item methods 66 | private TileModel getRandomEmptyTile(boolean threeWayTiles) { 67 | int index = Constants.random.nextInt(Constants.MAP_WIDTH * Constants.MAP_HEIGHT); 68 | int x = index / Constants.MAP_WIDTH; 69 | int y = index % Constants.MAP_HEIGHT; 70 | Vector2 pos = new Vector2(x, y); 71 | while (isCenterOrPlayerBase(pos) || getTile(pos).hasItem() || 72 | //only checks if the tile is 3+ when required 73 | (!getTile(pos).isThreeWayPlus() && threeWayTiles)) 74 | return getRandomEmptyTile(threeWayTiles); 75 | return getTile(pos); 76 | } 77 | 78 | public void placeItems(List> itemList, boolean threeWayTiles) { 79 | assert itemList.size() == 2; 80 | assert itemList.get(0).size() == itemList.get(1).size(); 81 | 82 | int numItems = itemList.get(0).size(); 83 | 84 | for (int i = 0; i < numItems; i++) { 85 | TileModel playerTile = getRandomEmptyTile(threeWayTiles); 86 | TileModel opponentTile = getTile(getOppositeTilePos(playerTile.getPos())); 87 | playerTile.setItem(itemList.get(Constants.PLAYER_INDEX).get(i)); 88 | opponentTile.setItem(itemList.get(Constants.OPPONENT_INDEX).get(i)); 89 | tilesWithItems.addAll(Arrays.asList(playerTile, opponentTile)); 90 | } 91 | Collections.shuffle(tilesWithItems, Constants.random); 92 | } 93 | 94 | public void removeItem(TileModel tile) { 95 | assert tile.hasItem(); 96 | tile.removeItem(); 97 | tilesWithItems.remove(tile); 98 | } 99 | 100 | //Tile methods 101 | private void setTile(int x, int y, TileModel tile) { 102 | tiles[x][y] = tile; 103 | } 104 | 105 | private void setTile(Vector2 pos, TileModel tile) { 106 | setTile(pos.getX(), pos.getY(), tile); 107 | } 108 | 109 | public TileModel getTile(int x, int y) { 110 | return isValidPos(new Vector2(x, y)) ? tiles[x][y] : null; 111 | } 112 | 113 | public TileModel getTile(Vector2 pos) { 114 | return getTile(pos.getX(), pos.getY()); 115 | } 116 | 117 | //Pushing methods 118 | public TileModel pushLine(TileModel pushedTile, int lineId, Constants.Direction dir) { 119 | pushedTile.push(dir); 120 | TileModel popped; 121 | if (dir == Constants.Direction.UP) popped = pushUp(pushedTile, lineId); 122 | else if (dir == Constants.Direction.RIGHT) popped = pushRight(pushedTile, lineId); 123 | else if (dir == Constants.Direction.DOWN) popped = pushDown(pushedTile, lineId); 124 | else popped = pushLeft(pushedTile, lineId); 125 | popped.pop(dir); 126 | return popped; 127 | } 128 | 129 | private TileModel pushUp(TileModel pushedTile, int col) { 130 | int maxRow = Constants.MAP_HEIGHT - 1; 131 | TileModel poppedTile = getTile(col, 0); 132 | 133 | for (int i = 0; i < maxRow; i++) { 134 | setTile(col, i, getTile(col,i + 1)); 135 | getTile(col, i).move(Constants.Direction.UP); 136 | } 137 | setTile(col, maxRow, pushedTile); 138 | getTile(col, maxRow).move(new Vector2(col, maxRow)); 139 | 140 | return poppedTile; 141 | } 142 | 143 | private TileModel pushRight(TileModel pushedTile, int row) { 144 | int maxCol = Constants.MAP_WIDTH - 1; 145 | TileModel poppedTile = getTile(maxCol, row); 146 | 147 | for (int i = maxCol; i > 0; i--) { 148 | setTile(i, row, getTile(i - 1, row)); 149 | getTile(i, row).move(Constants.Direction.RIGHT); 150 | } 151 | setTile(0, row, pushedTile); 152 | getTile(0, row).move(new Vector2(0, row)); 153 | return poppedTile; 154 | } 155 | 156 | private TileModel pushDown(TileModel pushedTile, int col) { 157 | int maxRow = Constants.MAP_HEIGHT - 1; 158 | TileModel poppedTile = getTile(col, maxRow); 159 | 160 | for (int i = maxRow; i > 0; i--) { 161 | setTile(col, i, getTile(col,i - 1)); 162 | getTile(col, i).move(Constants.Direction.DOWN); 163 | } 164 | setTile(col, 0, pushedTile); 165 | getTile(col, 0).move(new Vector2(col, 0)); 166 | return poppedTile; 167 | } 168 | 169 | private TileModel pushLeft(TileModel pushedTile, int row) { 170 | int maxCol = Constants.MAP_WIDTH - 1; 171 | TileModel poppedTile = getTile(0, row); 172 | for (int i = 0; i < maxCol; i++) { 173 | setTile(i, row, getTile(i + 1, row)); 174 | getTile(i, row).move(Constants.Direction.LEFT); 175 | } 176 | setTile(maxCol, row, pushedTile); 177 | getTile(maxCol, row).move(new Vector2(maxCol, row)); 178 | return poppedTile; 179 | } 180 | 181 | //Checkers 182 | public boolean isValidMove(Vector2 pos, Constants.Direction direction) { 183 | Vector2 newPos = new Vector2(pos); 184 | if (getTile(newPos).hasDirection(direction)) { 185 | newPos.add(direction.asVector()); 186 | if (isValidPos(newPos) && getTile(newPos).hasDirection(direction.getOpposite())) 187 | return true; 188 | } 189 | return false; 190 | } 191 | 192 | private boolean isValidPos(Vector2 pos) { 193 | return (pos.getX() >= 0 && pos.getY() >= 0 && 194 | pos.getX() < Constants.MAP_WIDTH && pos.getY() < Constants.MAP_HEIGHT); 195 | } 196 | 197 | private boolean isCenterOrPlayerBase(Vector2 pos) { 198 | return pos.equals(centerTilePos) || 199 | Constants.PLAYER_POSITIONS.contains(pos); 200 | } 201 | 202 | //Player input methods 203 | public void sendMapToPlayer(Player player) { 204 | for (int y = 0; y < Constants.MAP_HEIGHT; y++) { 205 | StringBuilder sb = new StringBuilder(); 206 | sb.append(getTile(0, y).patternToString()); 207 | for (int x = 1; x < Constants.MAP_WIDTH; x++) { 208 | sb.append(" " + getTile(x, y).patternToString()); 209 | } 210 | player.sendInputLine(sb.toString()); 211 | } 212 | } 213 | 214 | public void sendItemsToPlayer(Player player) { 215 | int numItems = tilesWithItems.size(); 216 | 217 | player.sendInputLine(Integer.toString(numItems)); 218 | for (TileModel tile : tilesWithItems) { 219 | if (player.getIndex() == Constants.PLAYER_INDEX) 220 | player.sendInputLine(tile.tileToString()); 221 | else 222 | player.sendInputLine(tile.opponentTileToString()); 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/Item.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import com.codingame.game.Utils.Constants; 4 | 5 | /** 6 | * Item is an immutable type representing the icon that can appear on a tile or card. 7 | * Each item is uniquely identified by its name and id of the player it can be acquired by. 8 | */ 9 | public class Item { 10 | private final String name; 11 | private final int playerId; 12 | private boolean highlight = false; 13 | private int highlightColor; 14 | 15 | private void checkRep() { 16 | assert name != null; 17 | assert Constants.ITEM_NAMES.contains(name); 18 | assert playerId == Constants.PLAYER_INDEX || playerId == Constants.OPPONENT_INDEX; 19 | } 20 | 21 | public Item(String name, int playerId) { 22 | this.name = name; 23 | this.playerId = playerId; 24 | checkRep(); 25 | } 26 | 27 | public String getName() { 28 | return this.name; 29 | } 30 | 31 | public int getPlayerId() { 32 | return this.playerId; 33 | } 34 | 35 | public int getOpponentId() { 36 | return (playerId == Constants.PLAYER_INDEX) ? Constants.OPPONENT_INDEX : Constants.PLAYER_INDEX; 37 | } 38 | 39 | //highlight methods 40 | public boolean getHighlight() { 41 | return highlight; 42 | } 43 | 44 | public void setHighlight() { 45 | highlight = true; 46 | } 47 | 48 | public int getHighlightColor() { 49 | return highlightColor; 50 | } 51 | 52 | public void setHighlightColor(int color) { 53 | highlightColor = color; 54 | } 55 | 56 | 57 | public boolean equals(Object obj) { 58 | if (!(obj instanceof Item)) { 59 | return false; 60 | } 61 | Item other = (Item)obj; 62 | return this.name.equals(other.name) && 63 | this.playerId == other.playerId; 64 | } 65 | 66 | public int hashCode() { 67 | int result = 17; 68 | result = 37 * result + playerId; 69 | result = 37 * result + name.hashCode(); 70 | return result; 71 | } 72 | 73 | public String itemToString() { 74 | return name + " " + playerId; 75 | } 76 | 77 | public String toTooltip() { 78 | return "item: " + itemToString(); 79 | } 80 | 81 | public String opponentItemToString() { 82 | return name + " " + getOpponentId(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/MovingModel.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import com.codingame.game.Utils.Constants; 4 | import com.codingame.game.Utils.Vector2; 5 | 6 | public abstract class MovingModel extends AbstractModel{ 7 | 8 | public MovingModel(Vector2 pos) { 9 | super(pos); 10 | } 11 | 12 | //move to a pos 13 | public void move(Vector2 pos) { 14 | super.setPos(pos); 15 | } 16 | 17 | //move in the specified direction 18 | public void move(Constants.Direction direction) { 19 | Vector2 pos = getPos(); 20 | pos.add(direction.asVector()); 21 | move(pos); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/PlayerModel.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import com.codingame.game.Player; 4 | import com.codingame.game.Utils.Constants; 5 | import com.codingame.game.Utils.Vector2; 6 | 7 | import java.util.*; 8 | 9 | public class PlayerModel extends MovingModel { 10 | public final int id; 11 | public final int orientation; 12 | private final Vector2 tilePos; 13 | private final Stack hiddenCards = new Stack<>(); 14 | private final List visibleCards = new ArrayList<>(); 15 | 16 | private final int maxNumCards = 3; //max number of cards to show 17 | private int numVisibleCards; 18 | private final Vector2 deckPosition; 19 | private final Vector2 cardPosition; 20 | 21 | private TileModel tile; 22 | 23 | public PlayerModel(int id) { 24 | super(Constants.PLAYER_POSITIONS.get(id)); 25 | this.id = id; 26 | orientation = (id == 0) ? 1 : -1; 27 | deckPosition = new Vector2(Constants.DECK_POSITIONS.get(id)); 28 | cardPosition = new Vector2(Constants.CARD_POSITIONS.get(id)); 29 | tilePos = Constants.TILE_MODEL_POSITIONS.get(id); 30 | } 31 | 32 | //Tile methods 33 | public void setTile(TileModel tile) { 34 | removeTile(); 35 | this.tile = tile; 36 | this.tile.setPos(tilePos); 37 | tile.setPlayerId(id); 38 | } 39 | 40 | private void removeTile() { 41 | if (tile != null) 42 | tile.setPlayerId(null); 43 | } 44 | 45 | public TileModel getTile() { 46 | return this.tile; 47 | } 48 | 49 | //Card methods 50 | public void setCards(List itemList) { 51 | //check if all items are unique 52 | assert itemList.size() == new HashSet<>(itemList).size(); 53 | 54 | Vector2 cardPos = new Vector2(deckPosition); 55 | for (Item item : itemList) { 56 | CardModel card = new CardModel(item, cardPos); 57 | //check if item belongs to the player 58 | assert card.getItem().getPlayerId() == id; 59 | hiddenCards.add(card); 60 | } 61 | } 62 | 63 | public List getCards() { 64 | return Collections.unmodifiableList(hiddenCards); 65 | } 66 | 67 | public void setNumVisibleCards(int numVisibleCards) { 68 | this.numVisibleCards = (numVisibleCards <= maxNumCards) ? numVisibleCards : maxNumCards; 69 | } 70 | 71 | private void adjustCardsPosition() { 72 | if (visibleCards.isEmpty()) { 73 | return; 74 | } 75 | int orientation = (id == 0) ? 1 : -1; 76 | Vector2 cardPos = new Vector2(cardPosition); 77 | int layer = 0; 78 | for (int i = 0; i < visibleCards.size(); i++) { 79 | CardModel card = visibleCards.get(i); 80 | card.move(cardPos); 81 | card.updatePosition(); 82 | layer++; 83 | card.setCardLayer(layer); 84 | cardPos.setX(cardPos.getX() + orientation * (Constants.CARD_SIZE + Constants.CARDS_OFFSET_X)); 85 | } 86 | } 87 | 88 | private int getNumCards() { 89 | return hiddenCards.size() + visibleCards.size(); 90 | } 91 | 92 | public int getNumDeckCards() { 93 | return hiddenCards.size(); 94 | } 95 | 96 | public int getNumQuestCards() { 97 | return visibleCards.size(); 98 | } 99 | 100 | public boolean removeCard(Item item){ 101 | for (CardModel card : visibleCards) { 102 | if (item.equals(card.getItem())) { 103 | card.remove(); 104 | visibleCards.remove(card); 105 | adjustCardsPosition(); 106 | return true; 107 | } 108 | } 109 | return false; 110 | } 111 | 112 | private void flipCard() { 113 | if (!hiddenCards.isEmpty()) { 114 | CardModel card = hiddenCards.pop(); 115 | card.flip(); 116 | visibleCards.add(card); 117 | } 118 | } 119 | 120 | //Flip the number of cards required to have the specified number of visible cards 121 | public void flipCards() { 122 | if (visibleCards.size() < numVisibleCards) { 123 | int availableCards = Math.min(numVisibleCards - visibleCards.size(), hiddenCards.size()); 124 | for (int i = 0; i < availableCards; i++) { 125 | flipCard(); 126 | } 127 | adjustCardsPosition(); 128 | } 129 | } 130 | 131 | //Player input methods 132 | public String playerToString() { 133 | return getNumCards() + " " + getPos().toString() + " " + tile.patternToString(); 134 | } 135 | 136 | public void sendCardsToPlayer(Player player) { 137 | for (CardModel card : visibleCards) { 138 | if (player.getIndex() == Constants.PLAYER_INDEX) 139 | player.sendInputLine(card.cardToString()); 140 | else 141 | player.sendInputLine(card.opponentCardToString()); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/StateUpdates/CardPositionUpdate.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model.StateUpdates; 2 | 3 | public class CardPositionUpdate {} 4 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/StateUpdates/FlipCardUpdate.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model.StateUpdates; 2 | 3 | public class FlipCardUpdate {} 4 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/StateUpdates/PoppedUpdate.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model.StateUpdates; 2 | 3 | import com.codingame.game.Utils.Constants.Direction; 4 | 5 | public class PoppedUpdate { 6 | private Direction direction; 7 | 8 | public PoppedUpdate(Direction direction) { 9 | this.direction = direction; 10 | } 11 | 12 | public Direction getDirection() { 13 | return direction; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/StateUpdates/PushedUpdate.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model.StateUpdates; 2 | 3 | import com.codingame.game.Utils.Constants.Direction; 4 | 5 | public class PushedUpdate { 6 | private Direction direction; 7 | 8 | public PushedUpdate(Direction direction) { 9 | this.direction = direction; 10 | } 11 | 12 | public Direction getDirection() { 13 | return direction; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/StateUpdates/RemoveCardUpdate.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model.StateUpdates; 2 | 3 | public class RemoveCardUpdate {} 4 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/StateUpdates/RemoveItemUpdate.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model.StateUpdates; 2 | 3 | public class RemoveItemUpdate {} 4 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/StateUpdates/ShowFrameUpdate.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model.StateUpdates; 2 | 3 | public class ShowFrameUpdate {} 4 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Model/TileModel.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import com.codingame.game.Model.StateUpdates.PoppedUpdate; 4 | import com.codingame.game.Model.StateUpdates.PushedUpdate; 5 | import com.codingame.game.Model.StateUpdates.RemoveItemUpdate; 6 | import com.codingame.game.Model.StateUpdates.ShowFrameUpdate; 7 | import com.codingame.game.Utils.Constants; 8 | import com.codingame.game.Utils.Constants.Direction; 9 | import com.codingame.game.Utils.Vector2; 10 | 11 | public class TileModel extends MovingModel{ 12 | private static final int PATTERN_LENGTH = 4; 13 | 14 | private String pattern; 15 | private Item item; 16 | 17 | private Integer playerId; 18 | 19 | //pattern represents path directions (up, right, down, left) as 4 binary digits (1 for path, 0 for no path) 20 | private void checkRep() { 21 | assert pattern != null && pattern.matches("[0-1]{" + PATTERN_LENGTH + "}"); 22 | } 23 | 24 | public TileModel(String pattern, Vector2 pos) { 25 | super(pos); 26 | this.pattern = pattern; 27 | checkRep(); 28 | } 29 | 30 | //Player-related methods 31 | public void setPlayerId(Integer id) { 32 | playerId = id; 33 | } 34 | 35 | public Integer getPlayerId() { 36 | return playerId; 37 | } 38 | 39 | //Item methods 40 | public void setItem(Item item) { 41 | assert this.item == null; 42 | this.item = item; 43 | } 44 | 45 | public Item getItem() { 46 | return item; 47 | } 48 | 49 | public void removeItem() { 50 | assert item != null; 51 | item = null; 52 | updateState(new RemoveItemUpdate()); 53 | } 54 | 55 | public boolean hasItem() { 56 | return item != null; 57 | } 58 | 59 | //Pattern methods 60 | public String getPattern() { 61 | return pattern; 62 | } 63 | 64 | public boolean hasDirection(Constants.Direction direction) { 65 | return pattern.charAt(direction.asValue()) == '1'; 66 | } 67 | 68 | public void rotate(int numTimes) { 69 | int num = numTimes % PATTERN_LENGTH; 70 | pattern = pattern.substring(PATTERN_LENGTH - num) 71 | + pattern.substring(0, PATTERN_LENGTH - num); 72 | } 73 | 74 | //check if it's a 3- or 4-way tile 75 | public boolean isThreeWayPlus() { 76 | return this.getPattern().chars().filter(ch -> ch == '1').count() >= 3; 77 | } 78 | 79 | //Player input methods 80 | public String patternToString() { 81 | return pattern; 82 | } 83 | 84 | public String tileToString() { 85 | return item != null ? item.getName() + " " + getTilePos().toString() + " " + item.getPlayerId() : ""; 86 | } 87 | 88 | public String opponentTileToString() { 89 | assert item != null; 90 | return item.getName() + " " + getOpponentTilePos().toString() + " " + item.getOpponentId(); 91 | } 92 | 93 | private Vector2 getOpponentTilePos() { 94 | if (playerId != null) 95 | return Constants.TILE_MODEL_POSITIONS.get(1 - playerId); 96 | return this.getPos(); 97 | } 98 | 99 | private Vector2 getTilePos() { 100 | if (playerId != null) 101 | return Constants.TILE_MODEL_POSITIONS.get(playerId); 102 | return this.getPos(); 103 | } 104 | 105 | @Override 106 | public void move(Vector2 pos) { 107 | super.setPos(pos); 108 | updateState(new ShowFrameUpdate()); 109 | } 110 | public void push(Direction direction) { 111 | updateState(new PushedUpdate(direction)); 112 | } 113 | public void pop(Direction direction) { 114 | updateState(new PoppedUpdate(direction)); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Player.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import com.codingame.game.Model.PlayerModel; 4 | import com.codingame.gameengine.core.AbstractMultiplayerPlayer; 5 | 6 | public class Player extends AbstractMultiplayerPlayer { 7 | private int expectedOutputLines; 8 | private PlayerModel player; 9 | 10 | @Override 11 | public int getExpectedOutputLines() { 12 | return expectedOutputLines; 13 | } 14 | 15 | public void setExpectedOutputLines(int expectedOutputLines) { 16 | this.expectedOutputLines = expectedOutputLines; 17 | } 18 | 19 | public PlayerModel createPlayer() { 20 | player = new PlayerModel(this.getIndex()); 21 | return player; 22 | } 23 | 24 | public PlayerModel getPlayer() { 25 | return player; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Utils/Constants.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.Random; 7 | import java.util.regex.Pattern; 8 | 9 | public class Constants { 10 | public static Random random; 11 | 12 | public static final int SCREEN_WIDTH = 1920; 13 | public static final int SCREEN_HEIGHT = 1080; 14 | 15 | public static final int TILE_SIZE = 128; 16 | public static final int TILES_OFFSET = 5; 17 | 18 | public static final int MAP_SIZE = 7; 19 | public static final int MAP_WIDTH = MAP_SIZE; 20 | public static final int MAP_HEIGHT = MAP_SIZE; 21 | 22 | public static final int MAP_POS_X = SCREEN_WIDTH / 2 - (MAP_HEIGHT * TILE_SIZE) / 2 23 | + TILE_SIZE / 2 - TILES_OFFSET / 2 * MAP_HEIGHT; 24 | public static final int MAP_POS_Y = SCREEN_HEIGHT / 2 - (MAP_WIDTH * TILE_SIZE) / 2 25 | + TILE_SIZE / 2 - TILES_OFFSET / 2 * MAP_WIDTH; 26 | 27 | public static final int DECK_OFFSET = 13; 28 | public static final int CARD_SIZE = 100; 29 | public static final int CARDS_OFFSET_X = 5; 30 | 31 | //horizontal coordinates for players' stuff 32 | public static final int PLAYER_CARD_POS_X = (MAP_POS_X - TILE_SIZE / 2) / 2; 33 | public static final int OPPONENT_CARD_POS_X = SCREEN_WIDTH - PLAYER_CARD_POS_X; 34 | 35 | public static final int PLAYER_CARD_POS_Y = MAP_POS_Y; 36 | public static final int OPPONENT_CARD_POS_Y = SCREEN_HEIGHT - PLAYER_CARD_POS_Y; 37 | 38 | public static final int PLAYER_DECK_POS_X = (MAP_POS_X - TILE_SIZE / 2) / 2; 39 | public static final int OPPONENT_DECK_POS_X = SCREEN_WIDTH - PLAYER_DECK_POS_X; 40 | 41 | public static final int PLAYER_DECK_POS_Y = PLAYER_CARD_POS_Y + CARD_SIZE + DECK_OFFSET; 42 | public static final int OPPONENT_DECK_POS_Y = SCREEN_HEIGHT - PLAYER_DECK_POS_Y; 43 | 44 | public static final int PLAYER_TILE_POS_Y = SCREEN_HEIGHT / 2; 45 | public static final int OPPONENT_TILE_POS_Y = SCREEN_HEIGHT - PLAYER_TILE_POS_Y; 46 | 47 | public static final List ITEM_NAMES = Arrays.asList( 48 | "ARROW", "BOOK", "CANE", "CANDY", "DIAMOND", "FISH", "MASK", "KEY", "POTION", "SCROLL", "SHIELD", "SWORD" 49 | ); 50 | 51 | public static final List> TILE_PATTERNS = new ArrayList<>(Arrays.asList( 52 | Arrays.asList("1111", "1111", "1111", "1111", "1111", "1111", "1111", "1111", 53 | "1111", "1111", "1111", "1111", "1111", "1111", "1111", "1111", 54 | "1111", "1111", "1111", "1111", "1111", "1111", "1111", "1111"), 55 | 56 | Arrays.asList("0110", "0110", "0110", "0110", "0110", "0110", "0110", "0110", 57 | "1101", "1101", "1101", "0111", "0111", "0111", "0111", "0111", "0111", 58 | "1010", "1010", "1010", "1010", "1010", "1010", 59 | "1111"), 60 | 61 | Arrays.asList("0110", "0110", "0110", "0110", "0110", "0110", "0110", "0110", 62 | "1101", "1101", "1101", "0111", "0111", "0111", "0111", 63 | "1010", "1010", "1010", "1010", "1010", "1010", "1010", "1010", "1010"), 64 | 65 | Arrays.asList("0110", "0110", "0110", "0110", "0110", "0110", "0110", "0110", "0110", "0110", 66 | "1101", "1101", "1101", "0111", "0111", 67 | "1010", "1010", "1010", "1010", "1010", "1010", "1010", "1010", "1010") 68 | )); 69 | 70 | public static final int PLAYER_INDEX = 0; 71 | public static final int OPPONENT_INDEX = 1; 72 | public static final int NUM_PLAYERS = 2; 73 | 74 | public static final List PLAYER_POSITIONS = Arrays.asList( 75 | new Vector2(0, 0), 76 | new Vector2(MAP_WIDTH - 1, MAP_HEIGHT - 1) 77 | ); 78 | 79 | public static final List DECK_POSITIONS = Arrays.asList( 80 | new Vector2(PLAYER_DECK_POS_X, PLAYER_DECK_POS_Y), 81 | new Vector2(OPPONENT_DECK_POS_X, OPPONENT_DECK_POS_Y) 82 | ); 83 | 84 | public static final List CARD_POSITIONS = Arrays.asList( 85 | new Vector2(PLAYER_CARD_POS_X, PLAYER_CARD_POS_Y), 86 | new Vector2(OPPONENT_CARD_POS_X, OPPONENT_CARD_POS_Y) 87 | ); 88 | 89 | public static final List TILE_POSITIONS = Arrays.asList( 90 | new Vector2(PLAYER_DECK_POS_X, PLAYER_TILE_POS_Y), 91 | new Vector2(OPPONENT_DECK_POS_X, OPPONENT_TILE_POS_Y) 92 | ); 93 | 94 | public static final List TILE_MODEL_POSITIONS = Arrays.asList( 95 | Vector2.MINUS_ONE, Vector2.MINUS_TWO); 96 | 97 | //make sure the number of GAME turns is EVEN for equal number of PUSH and MOVE turns 98 | public static final int MAX_GAME_TURNS = 150; 99 | public static final int MAX_MOVE_STEPS = 20; 100 | public static final int MAX_INPUT_LENGTH = 124; 101 | 102 | public static final Pattern PLAYER_INPUT_PUSH_PATTERN = Pattern 103 | .compile("(?\\bPUSH\\b) (?[ 0-" + (MAP_SIZE - 1) + "]) (?(\\bUP\\b|\\bRIGHT\\b|\\bDOWN\\b|\\bLEFT\\b))"); 104 | public static final Pattern PLAYER_INPUT_MOVE_PATTERN = Pattern 105 | .compile(String.format("(?:\\bMOVE\\b)((?: )(?(\\bUP\\b|\\bRIGHT\\b|\\bDOWN\\b|\\bLEFT\\b))){1,%d}", MAX_MOVE_STEPS)); 106 | public static final Pattern PLAYER_INPUT_MOVE_MAX_STEPS_PATTERN = Pattern 107 | .compile(String.format("(?:\\bMOVE\\b)((?: )(?(\\bUP\\b|\\bRIGHT\\b|\\bDOWN\\b|\\bLEFT\\b))){%d,}", MAX_MOVE_STEPS)); 108 | public static final Pattern PLAYER_INPUT_MOVE_STEPS_PATTERN = Pattern 109 | .compile("(?(?:\\bUP\\b|\\bRIGHT\\b|\\bDOWN\\b|\\bLEFT\\b))"); 110 | public static final Pattern PLAYER_INPUT_PASS_PATTERN = Pattern 111 | .compile("PASS"); 112 | 113 | public enum Direction { 114 | UP(Vector2.UP, 0), 115 | RIGHT(Vector2.RIGHT, 1), 116 | DOWN(Vector2.DOWN, 2), 117 | LEFT(Vector2.LEFT, 3); 118 | 119 | private final Vector2 vector; 120 | private final int value; 121 | 122 | Direction(Vector2 vector, int value) { 123 | this.vector = vector; 124 | this.value = value; 125 | } 126 | public Vector2 asVector() { 127 | return vector; 128 | } 129 | 130 | public int asValue() { 131 | return value; 132 | } 133 | 134 | public Direction getOpposite() { 135 | switch(this) { 136 | case UP: return DOWN; 137 | case DOWN: return UP; 138 | case LEFT: return RIGHT; 139 | case RIGHT: return LEFT; 140 | } 141 | return null; 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Utils/Vector2.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Utils; 2 | 3 | /** 4 | * Vector2 is a mutable type representing a position in x,y coordinates. 5 | */ 6 | public class Vector2 { 7 | private int x; 8 | private int y; 9 | 10 | /** 11 | * Constants to be used when sending item positions as input, 12 | * MINUS_ONE for item on player's tile, MINUS_TWO for item on opponent's tile. 13 | */ 14 | public static final Vector2 MINUS_ONE = new Vector2(-1, -1); 15 | public static final Vector2 MINUS_TWO = new Vector2(-2, -2); 16 | 17 | /** 18 | * Constants to be used when moving on the map. 19 | */ 20 | public static final Vector2 UP = new Vector2(0, -1); 21 | public static final Vector2 DOWN = new Vector2(0, 1); 22 | public static final Vector2 LEFT = new Vector2(-1, 0); 23 | public static final Vector2 RIGHT = new Vector2(1, 0); 24 | 25 | public Vector2(int x, int y) { 26 | this.x = x; 27 | this.y = y; 28 | } 29 | 30 | public Vector2(Vector2 other) { 31 | this.x = other.x; 32 | this.y = other.y; 33 | } 34 | 35 | public void setX(int x) { 36 | this.x = x; 37 | } 38 | 39 | public int getX() { 40 | return x; 41 | } 42 | 43 | public void setY(int y) { 44 | this.y = y; 45 | } 46 | 47 | public int getY() { 48 | return y; 49 | } 50 | 51 | public void add(Vector2 other) { 52 | this.x += other.x; 53 | this.y += other.y; 54 | } 55 | 56 | /** 57 | * Adds all attributes of a given Vector2 object to the current one and wraps around map borders when required. 58 | * @param other the Vector2 object to add to the current one. 59 | */ 60 | public void wrap(Vector2 other) { 61 | this.x = (this.x + other.x + Constants.MAP_WIDTH) % Constants.MAP_WIDTH; 62 | this.y = (this.y + other.y + Constants.MAP_HEIGHT) % Constants.MAP_HEIGHT; 63 | } 64 | 65 | public String toString() { 66 | return x + " " + y; 67 | } 68 | 69 | public String toTooltip() { 70 | return "pos: (" + x + ", " + y + ")"; 71 | } 72 | 73 | public boolean equals(Object obj) { 74 | if (!(obj instanceof Vector2)) 75 | return false; 76 | Vector2 other = (Vector2) obj; 77 | return this.x == other.x && this.y == other.y; 78 | } 79 | public static Vector2 fromMapSpaceToViewSpace(Vector2 pos) { 80 | int x = Constants.MAP_POS_X + pos.getX() * (Constants.TILE_SIZE + Constants.TILES_OFFSET); 81 | int y = Constants.MAP_POS_Y + pos.getY() * (Constants.TILE_SIZE + Constants.TILES_OFFSET); 82 | return new Vector2(x,y); 83 | } 84 | public static Vector2 fromViewSpaceToMapSpace(Vector2 pos) { 85 | int x = (pos.getX() - Constants.MAP_POS_X) / (Constants.TILE_SIZE + Constants.TILES_OFFSET); 86 | int y = (pos.getY() - Constants.MAP_POS_Y) / (Constants.TILE_SIZE + Constants.TILES_OFFSET); 87 | return new Vector2(x,y); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/AbstractView.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import java.util.Observable; 4 | import java.util.Observer; 5 | 6 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 7 | 8 | public abstract class AbstractView implements Observer { 9 | protected GraphicEntityModule entityModule; 10 | private boolean disposable = false; 11 | 12 | public AbstractView(GraphicEntityModule entityManager) { 13 | this.entityModule = entityManager; 14 | } 15 | 16 | public abstract void updateView(); 17 | 18 | public boolean isDisposable() { 19 | return disposable; 20 | } 21 | 22 | public void doDispose() { 23 | disposable = true; 24 | } 25 | 26 | public void update(Observable observable, Object arg) {} 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/ArrowView.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import com.codingame.game.Utils.Constants; 4 | import com.codingame.game.Utils.Vector2; 5 | import com.codingame.gameengine.module.entities.Entity; 6 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 7 | import com.codingame.gameengine.module.entities.Sprite; 8 | 9 | public class ArrowView extends AbstractView { 10 | private final Vector2 pos; 11 | private final double rotation; 12 | 13 | private Sprite sprite; 14 | 15 | public ArrowView(GraphicEntityModule entityModule, Vector2 pos, double rotation) { 16 | super(entityModule); 17 | this.pos = pos; 18 | this.rotation = rotation; 19 | 20 | createArrowView(); 21 | } 22 | 23 | private void createArrowView() { 24 | sprite = entityModule.createSprite() 25 | .setImage(String.format("arrow_%d.png", 0)) 26 | .setX(pos.getX()) 27 | .setY(pos.getY()) 28 | .setBaseWidth(Constants.TILE_SIZE) 29 | .setBaseHeight(Constants.TILE_SIZE / 2) 30 | .setAnchor(0.5) 31 | .setZIndex(0) 32 | .setRotation(rotation) 33 | .setVisible(false); 34 | } 35 | 36 | public void updateView() {} 37 | 38 | public void showArrow(int type) { 39 | sprite.setImage(String.format("arrow_%d.png", type)) 40 | .setVisible(true); 41 | entityModule.commitEntityState(0, sprite); 42 | } 43 | 44 | public void hideArrow() { 45 | sprite.setVisible(false); 46 | entityModule.commitEntityState(0, sprite); 47 | } 48 | 49 | public Entity getEntity() { 50 | return sprite; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/BoardView.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import com.codingame.game.Model.CardModel; 4 | import com.codingame.game.Model.TileModel; 5 | import com.codingame.game.Player; 6 | import com.codingame.game.Utils.Constants; 7 | import com.codingame.game.Utils.Vector2; 8 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 9 | import com.codingame.view.tooltip.TooltipModule; 10 | import com.codingame.gameengine.module.entities.Group; 11 | 12 | import java.util.*; 13 | 14 | public class BoardView extends AbstractView{ 15 | private TooltipModule tooltipModule; 16 | 17 | //distance from arrows to tiles 18 | private final int arrowOffset = 100; 19 | //distance between arrows 20 | private final int arrowSpacing = Constants.TILE_SIZE + Constants.TILES_OFFSET; 21 | 22 | private Group group; 23 | 24 | List tiles; 25 | List players; 26 | List cards; 27 | Map arrows; 28 | 29 | List> arrowsToShow = new ArrayList<>(); 30 | List arrowsToHide = new ArrayList<>(); 31 | 32 | public BoardView(GraphicEntityModule entityModule, TooltipModule tooltipModule){ 33 | super(entityModule); 34 | this.tooltipModule = tooltipModule; 35 | 36 | tiles = new ArrayList<>(); 37 | players = new ArrayList<>(); 38 | cards = new ArrayList<>(); 39 | arrows = new HashMap<>(); 40 | 41 | this.group = this.entityModule.createGroup() 42 | .setX(0) 43 | .setY(0) 44 | .setVisible(true) 45 | .setZIndex(1) 46 | .setScale(1); 47 | 48 | createArrows(); 49 | } 50 | 51 | public TileView createTileView(TileModel tile) { 52 | TileView tileView = new TileView(entityModule, tooltipModule, tile); 53 | group.add(tileView.getEntity().setZIndex(1)); 54 | tiles.add(tileView); 55 | return tileView; 56 | } 57 | 58 | public PlayerView createPlayerView(Player player) { 59 | PlayerView playerView = new PlayerView(entityModule, player, player.getPlayer()); 60 | group.add(playerView.getEntity().setZIndex(2)); 61 | players.add(playerView); 62 | return playerView; 63 | } 64 | 65 | public CardView createCardView(CardModel card) { 66 | CardView cardView = new CardView(entityModule, tooltipModule, card); 67 | //cards are always below tiles 68 | group.add(cardView.getEntity().setZIndex(0)); 69 | cards.add(cardView); 70 | return cardView; 71 | } 72 | 73 | public void updateView() { 74 | for (ArrowView arrow : arrowsToHide) 75 | arrow.hideArrow(); 76 | arrowsToHide.clear(); 77 | 78 | if (arrowsToShow.size() == 2 && isSamePushAction()) 79 | //show two arrows pushing the same line 80 | showArrow(new AbstractMap.SimpleEntry<>(arrowsToShow.get(0).getKey(), 2)); 81 | else 82 | arrowsToShow.stream().forEach(arrow -> showArrow(arrow)); 83 | arrowsToShow.clear(); 84 | } 85 | 86 | private boolean isSamePushAction() { 87 | return arrowsToShow.stream() 88 | .map(arrow -> arrow.getKey()) 89 | .distinct() 90 | .count() == 1; 91 | } 92 | 93 | private void showArrow(AbstractMap.SimpleEntry arrow) { 94 | String arrowId = arrow.getKey(); 95 | Integer arrowType = arrow.getValue(); 96 | ArrowView arrowView = arrows.get(arrowId); 97 | arrowView.showArrow(arrowType); 98 | arrowsToHide.add(arrowView); 99 | } 100 | 101 | public void update(AbstractMap.SimpleEntry action) { 102 | arrowsToShow.add(action); 103 | } 104 | 105 | private void createArrows() { 106 | createUpArrows(); 107 | createRightArrows(); 108 | createDownArrows(); 109 | createLeftArrows(); 110 | } 111 | 112 | private void createUpArrows() { 113 | Vector2 pos = new Vector2(Constants.MAP_POS_X, 114 | Constants.MAP_POS_Y + arrowSpacing * (Constants.MAP_HEIGHT - 1) + arrowOffset); 115 | for (int x = 0; x < Constants.MAP_WIDTH; x++) { 116 | String id = x + "" + Constants.Direction.UP.asValue(); 117 | ArrowView view = new ArrowView(entityModule, pos, Math.toRadians(0)); 118 | group.add(view.getEntity().setZIndex(0)); 119 | arrows.put(id, view); 120 | pos.setX(pos.getX() + arrowSpacing); 121 | } 122 | } 123 | 124 | private void createRightArrows(){ 125 | Vector2 pos = new Vector2(Constants.MAP_POS_X - arrowOffset, Constants.MAP_POS_Y); 126 | for (int y = 0; y < Constants.MAP_HEIGHT; y++){ 127 | String id = y + "" + Constants.Direction.RIGHT.asValue(); 128 | ArrowView view = new ArrowView(entityModule, pos, Math.toRadians(90)); 129 | group.add(view.getEntity().setZIndex(0)); 130 | arrows.put(id, view); 131 | pos.setY(pos.getY() + arrowSpacing); 132 | } 133 | } 134 | 135 | private void createDownArrows() { 136 | Vector2 pos = new Vector2(Constants.MAP_POS_X, Constants.MAP_POS_Y - arrowOffset); 137 | for (int x = 0; x < Constants.MAP_WIDTH; x++) { 138 | String id = x + "" + Constants.Direction.DOWN.asValue(); 139 | ArrowView view = new ArrowView(entityModule, pos, Math.toRadians(180)); 140 | group.add(view.getEntity().setZIndex(0)); 141 | arrows.put(id, view); 142 | pos.setX(pos.getX() + arrowSpacing); 143 | } 144 | } 145 | 146 | private void createLeftArrows(){ 147 | Vector2 pos = new Vector2(Constants.MAP_POS_X + arrowSpacing * (Constants.MAP_WIDTH - 1) + arrowOffset, 148 | Constants.MAP_POS_Y); 149 | for (int y = 0; y < Constants.MAP_HEIGHT; y++){ 150 | String id = y + "" + Constants.Direction.LEFT.asValue(); 151 | ArrowView view = new ArrowView(entityModule, pos, Math.toRadians(270)); 152 | group.add(view.getEntity().setZIndex(0)); 153 | arrows.put(id, view); 154 | pos.setY(pos.getY() + arrowSpacing); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/CardDeckView.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import com.codingame.game.Model.PlayerModel; 4 | import com.codingame.game.Utils.Constants; 5 | import com.codingame.game.Utils.Vector2; 6 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 7 | import com.codingame.gameengine.module.entities.Group; 8 | import com.codingame.gameengine.module.entities.Sprite; 9 | import com.codingame.gameengine.module.entities.Text; 10 | 11 | public class CardDeckView extends AbstractView { 12 | private Sprite back; 13 | private Group group; 14 | private Text numCards; 15 | 16 | private PlayerModel player; 17 | private final Vector2 pos; 18 | 19 | public CardDeckView(GraphicEntityModule entityModule, PlayerModel player) { 20 | super(entityModule); 21 | this.player = player; 22 | pos = new Vector2(Constants.DECK_POSITIONS.get(player.id)); 23 | player.addObserver(this); 24 | 25 | createCardDeckView(); 26 | } 27 | 28 | private void createCardDeckView() { 29 | back = entityModule.createSprite() 30 | .setImage(String.format("cardBack_%d.png", player.id)) 31 | .setBaseWidth(Constants.CARD_SIZE) 32 | .setBaseHeight(Constants.CARD_SIZE) 33 | .setAnchor(0.5) 34 | .setZIndex(0); 35 | numCards = entityModule.createText("") 36 | .setFillColor(0x000000) 37 | .setFontSize(60) 38 | .setAnchor(0.5) 39 | .setZIndex(1); 40 | group = entityModule.createGroup() 41 | .setScale(1) 42 | .setX(0) 43 | .setY(0) 44 | .setZIndex(0); 45 | group.add(back, numCards); 46 | group.setX(pos.getX()).setY(pos.getY()); 47 | } 48 | 49 | public void updateView() { 50 | numCards.setText(String.valueOf(player.getNumDeckCards())); 51 | entityModule.commitEntityState(0, numCards); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/CardView.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import com.codingame.game.Referee; 4 | import com.codingame.game.Model.CardModel; 5 | import com.codingame.game.Model.Item; 6 | import com.codingame.game.Model.StateUpdates.CardPositionUpdate; 7 | import com.codingame.game.Model.StateUpdates.FlipCardUpdate; 8 | import com.codingame.game.Model.StateUpdates.RemoveCardUpdate; 9 | import com.codingame.game.Utils.Constants; 10 | import com.codingame.gameengine.module.entities.Entity; 11 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 12 | import com.codingame.gameengine.module.entities.Group; 13 | import com.codingame.gameengine.module.entities.Sprite; 14 | import com.codingame.view.tooltip.TooltipModule; 15 | 16 | import java.util.Observable; 17 | 18 | public class CardView extends MovingView { 19 | private TooltipModule tooltipModule; 20 | 21 | private Group group; 22 | private Sprite front; 23 | private Sprite item; 24 | 25 | private Item cardItem; 26 | private CardModel model; 27 | 28 | public CardView(GraphicEntityModule entityModule, TooltipModule tooltipModule, CardModel card) { 29 | super(entityModule); 30 | this.tooltipModule = tooltipModule; 31 | this.model = card; 32 | this.cardItem = model.getItem(); 33 | card.addObserver(this); 34 | 35 | createCardView(); 36 | tooltipModule.registerEntity(group); 37 | } 38 | 39 | private void createCardView() { 40 | front = entityModule.createSprite() 41 | .setImage(String.format("cardFront_%d.png", cardItem.getPlayerId())) 42 | .setBaseWidth(Constants.CARD_SIZE) 43 | .setBaseHeight(Constants.CARD_SIZE) 44 | .setAnchor(0.5) 45 | .setZIndex(0); 46 | item = entityModule.createSprite() 47 | .setImage(String.format("item_%s_%d", cardItem.getName(), cardItem.getPlayerId())) 48 | .setAnchor(0.5) 49 | .setZIndex(1); 50 | group = entityModule.createGroup() 51 | .setScale(1) 52 | .setX(0) 53 | .setY(0) 54 | .setVisible(false); 55 | group.add(front, item); 56 | } 57 | 58 | public void updateView() { 59 | group.setZIndex(model.cardLayer); 60 | entityModule.commitEntityState(0, group); 61 | group.setX(model.getPos().getX()).setY(model.getPos().getY()); 62 | entityModule.commitEntityState(350. / Referee.myGameManager.getFrameDuration(), group); 63 | tooltipModule.updateExtraTooltipText(group, model.getItem().toTooltip()); 64 | } 65 | 66 | private void flip() { 67 | group.setVisible(true); 68 | entityModule.commitEntityState(0, group); 69 | } 70 | 71 | private void updatePosition() { 72 | group.setX(model.getPos().getX()).setY(model.getPos().getY()); 73 | entityModule.commitEntityState(1, group); 74 | } 75 | 76 | private void removeCardView() { 77 | group.setAlpha(0); 78 | group.setZIndex(group.getZIndex() - 1); 79 | entityModule.commitEntityState(1, group); 80 | doDispose(); 81 | } 82 | 83 | public void update(Observable observable, Object update) { 84 | super.update(model, update); 85 | if (update instanceof FlipCardUpdate) { 86 | flip(); 87 | } else if (update instanceof CardPositionUpdate) { 88 | updatePosition(); 89 | } else if (update instanceof RemoveCardUpdate){ 90 | removeCardView(); 91 | } 92 | } 93 | 94 | public Entity getEntity() { 95 | return group; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/MovingView.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import com.codingame.game.Utils.Vector2; 4 | import com.codingame.gameengine.module.entities.Entity; 5 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 6 | 7 | public abstract class MovingView extends AbstractView { 8 | 9 | public MovingView(GraphicEntityModule entityModule) { 10 | super(entityModule); 11 | } 12 | 13 | public void setMapPos(Entity entity, Vector2 pos) { 14 | Vector2 newPos = Vector2.fromMapSpaceToViewSpace(pos); 15 | entity.setX(newPos.getX()).setY(newPos.getY()); 16 | } 17 | 18 | public abstract Entity getEntity(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/PlayerTextView.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import com.codingame.game.Player; 4 | import com.codingame.game.Referee; 5 | import com.codingame.game.Utils.Constants; 6 | import com.codingame.game.Utils.Vector2; 7 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 8 | import com.codingame.gameengine.module.entities.Group; 9 | import com.codingame.gameengine.module.entities.Sprite; 10 | import com.codingame.gameengine.module.entities.Text; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | 16 | public class PlayerTextView extends AbstractView { 17 | private final int AVATAR_SIZE = Constants.TILE_SIZE; 18 | 19 | private final int PLAYER_INFO_POS_Y = Constants.SCREEN_HEIGHT - 230; 20 | private final int OPPONENT_INFO_POS_Y = Constants.SCREEN_HEIGHT - PLAYER_INFO_POS_Y; 21 | 22 | private List INFO_POS = Arrays.asList(new Vector2(Constants.PLAYER_DECK_POS_X, PLAYER_INFO_POS_Y), 23 | new Vector2(Constants.OPPONENT_DECK_POS_X, OPPONENT_INFO_POS_Y)); 24 | 25 | private final int NAME_VERTICAL_OFFSET = 110; 26 | private int BACKGROUND_VERTICAL_OFFSET = 123; 27 | private List BACKGROUND_HORIZONTAL_OFFSET = Arrays.asList(-15, 0); 28 | 29 | private Group group; 30 | private Text name; 31 | private Sprite avatar; 32 | private Sprite background; 33 | 34 | private Player player; 35 | private Vector2 pos; 36 | private int id; 37 | private int orientation; 38 | 39 | 40 | public PlayerTextView(GraphicEntityModule entityModule, Player player){ 41 | super(entityModule); 42 | this.player = player; 43 | id = player.getIndex(); 44 | pos = INFO_POS.get(id); 45 | orientation = this.player.getPlayer().orientation; 46 | 47 | createPlayerText(); 48 | } 49 | 50 | private void createPlayerText() { 51 | avatar = entityModule.createSprite() 52 | .setX(0) 53 | .setY(0) 54 | .setZIndex(1) 55 | .setImage(player.getAvatarToken()) 56 | .setBaseWidth(AVATAR_SIZE) 57 | .setBaseHeight(AVATAR_SIZE) 58 | .setAnchor(0.5); 59 | //reconsider the length param when changing font family or font size 60 | name = entityModule.createText(player.getNicknameToken()) 61 | .setX(0) 62 | .setY(NAME_VERTICAL_OFFSET * orientation) 63 | .setZIndex(1) 64 | .setFillColor(player.getColorToken()) 65 | .setFontSize(40) 66 | .setFontFamily("Arial Black") 67 | .setAnchor(0.5); 68 | Referee.nicksModule.registerNickname(name); 69 | background = entityModule.createSprite() 70 | .setImage(String.format("background_name_%d.png", id)) 71 | .setX(BACKGROUND_HORIZONTAL_OFFSET.get(player.getIndex())) 72 | .setY(BACKGROUND_VERTICAL_OFFSET * orientation) 73 | .setZIndex(0) 74 | .setAnchor(0.5); 75 | group = entityModule.createGroup() 76 | .setX(pos.getX()) 77 | .setY(pos.getY()) 78 | .setScale(1); 79 | group.add(avatar, name, background); 80 | } 81 | 82 | public void updateView() {} 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/PlayerView.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import com.codingame.game.Model.PlayerModel; 4 | import com.codingame.game.Player; 5 | import com.codingame.game.Referee; 6 | import com.codingame.game.InputActions.Action; 7 | import com.codingame.gameengine.module.entities.Entity; 8 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 9 | import com.codingame.gameengine.module.entities.Sprite; 10 | 11 | public class PlayerView extends MovingView{ 12 | private Sprite sprite; 13 | 14 | private Player player; 15 | private PlayerModel model; 16 | 17 | public PlayerView(GraphicEntityModule entityModule, Player player, PlayerModel model) { 18 | super(entityModule); 19 | this.player = player; 20 | this.model = model; 21 | model.addObserver(this); 22 | 23 | createPlayerView(); 24 | } 25 | 26 | private void createPlayerView() { 27 | sprite = entityModule.createSprite() 28 | .setImage(String.format("elf_%d.png", player.getIndex())) 29 | .setAnchor(0.5); 30 | } 31 | 32 | public void updateView(){ 33 | if(Referee.turnType == Action.Type.PUSH) { 34 | entityModule.commitEntityState(0.3, sprite); 35 | setMapPos(sprite, model.getPos()); 36 | entityModule.commitEntityState(0.6, sprite); 37 | } 38 | setMapPos(sprite, model.getPos()); 39 | } 40 | 41 | public Entity getEntity() { 42 | return sprite; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/TileView.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import com.codingame.game.Model.Item; 4 | import com.codingame.game.Model.StateUpdates.PoppedUpdate; 5 | import com.codingame.game.Model.StateUpdates.PushedUpdate; 6 | import com.codingame.game.Model.StateUpdates.RemoveItemUpdate; 7 | import com.codingame.game.Model.StateUpdates.ShowFrameUpdate; 8 | import com.codingame.game.Model.TileModel; 9 | import com.codingame.game.Utils.Constants; 10 | import com.codingame.game.Utils.Constants.Direction; 11 | import com.codingame.game.Utils.Vector2; 12 | import com.codingame.gameengine.module.entities.Entity; 13 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 14 | import com.codingame.gameengine.module.entities.Group; 15 | import com.codingame.gameengine.module.entities.Sprite; 16 | import com.codingame.gameengine.module.entities.Circle; 17 | import com.codingame.view.tooltip.TooltipModule; 18 | 19 | import java.util.Observable; 20 | 21 | public class TileView extends MovingView { 22 | private TooltipModule tooltipModule; 23 | 24 | private Group group; 25 | 26 | private Sprite decors; 27 | private Sprite directions; 28 | private Sprite background; 29 | private Sprite frame; 30 | private Group itemGroup; 31 | private Circle itemBackground; 32 | 33 | private boolean showFrame = false; 34 | private boolean highlight = false; 35 | 36 | private Item tileItem; 37 | private TileModel model; 38 | private TileState state = TileState.STILL; 39 | 40 | private enum TileState{ 41 | STILL, 42 | MOVED, 43 | POPPED, 44 | PUSHED; 45 | } 46 | 47 | private Direction tempDirection; 48 | 49 | public TileView(GraphicEntityModule entityModule, TooltipModule tooltipModule, TileModel tile) { 50 | super(entityModule); 51 | this.tooltipModule = tooltipModule; 52 | this.model = tile; 53 | this.tileItem = model.getItem(); 54 | tile.addObserver(this); 55 | 56 | createTileView(); 57 | tooltipModule.registerEntity(group); 58 | } 59 | 60 | private void createTileView() { 61 | decors = entityModule.createSprite() 62 | .setImage(String.format("decors_%s", model.getPattern())) 63 | .setBaseWidth(Constants.TILE_SIZE) 64 | .setBaseHeight(Constants.TILE_SIZE) 65 | .setAnchor(0.5) 66 | .setZIndex(1); 67 | directions = entityModule.createSprite() 68 | .setImage(String.format("paths_%s", model.getPattern())) 69 | .setBaseWidth(Constants.TILE_SIZE) 70 | .setBaseHeight(Constants.TILE_SIZE) 71 | .setAnchor(0.5) 72 | .setZIndex(2); 73 | frame = entityModule.createSprite() 74 | .setImage("frame.png") 75 | .setBaseWidth(Constants.TILE_SIZE) 76 | .setBaseHeight(Constants.TILE_SIZE) 77 | .setAnchor(0.5) 78 | .setZIndex(2) 79 | .setVisible(false); 80 | background = entityModule.createSprite() 81 | .setImage("tile_background.png") 82 | .setBaseWidth(Constants.TILE_SIZE) 83 | .setBaseHeight(Constants.TILE_SIZE) 84 | .setAlpha(0.3) 85 | .setAnchor(0.5) 86 | .setZIndex(0); 87 | group = entityModule.createGroup() 88 | .setScale(1); 89 | group.add(decors, directions, frame, background); 90 | 91 | addItem(); 92 | } 93 | 94 | private void addItem() { 95 | if (tileItem != null) { 96 | itemGroup = entityModule.createGroup().setZIndex(2); 97 | itemBackground = entityModule.createCircle() 98 | .setZIndex(0) 99 | .setScale(0.3) 100 | .setAlpha(0.7); 101 | itemGroup.add(itemBackground); 102 | String spritePath = String.format("item_%s_%d", tileItem.getName(), tileItem.getPlayerId()); 103 | itemGroup.add(entityModule.createSprite() 104 | .setImage(spritePath) 105 | .setAnchor(0.5) 106 | .setZIndex(1)); 107 | group.add(itemGroup); 108 | } 109 | } 110 | 111 | private void removeItem() { 112 | itemGroup.setScale(0); 113 | entityModule.commitEntityState(1, itemGroup); 114 | group.remove(this.itemGroup); 115 | } 116 | 117 | public void updateView(){ 118 | if (model.getPlayerId() != null) { 119 | //always show frame for player's tiles 120 | frame.setVisible(true); 121 | entityModule.commitEntityState(0, frame); 122 | 123 | 124 | if (this.state == TileState.POPPED) { 125 | entityModule.commitEntityState(0.3, group); 126 | Vector2 nextPos = Vector2.fromViewSpaceToMapSpace(new Vector2(group.getX(),group.getY())); 127 | nextPos.add(tempDirection.asVector()); 128 | setMapPos(group, nextPos); 129 | //player tile is on top of everything 130 | group.setZIndex(4); 131 | entityModule.commitEntityState(0.6, group); 132 | 133 | } 134 | Vector2 pos = Constants.TILE_POSITIONS.get(model.getPlayerId()); 135 | group.setX(pos.getX()).setY(pos.getY()); 136 | } else { 137 | //only show frames for moving map tiles 138 | if (showFrame) { 139 | frame.setVisible(true); 140 | entityModule.commitEntityState(0, frame); 141 | frame.setVisible(false); 142 | showFrame = false; 143 | } 144 | if (this.state == TileState.PUSHED) { 145 | //player tile is on top of everything 146 | group.setZIndex(4); 147 | entityModule.commitEntityState(0, group); 148 | Vector2 nextPos = new Vector2(model.getPos()); 149 | nextPos.add(tempDirection.getOpposite().asVector()); 150 | setMapPos(group, nextPos); 151 | entityModule.commitEntityState(0.3, group); 152 | //get pushed tile zIndex back 153 | group.setZIndex(1); 154 | setMapPos(group, model.getPos()); 155 | entityModule.commitEntityState(0.6, group); 156 | } else if (this.state == TileState.MOVED) { 157 | entityModule.commitEntityState(0.3, group); 158 | setMapPos(group, model.getPos()); 159 | entityModule.commitEntityState(0.6, group); 160 | } 161 | setMapPos(group, model.getPos()); 162 | 163 | } 164 | 165 | this.state = TileState.STILL; 166 | 167 | String tooltipText = model.getPos().toTooltip(); 168 | if (model.hasItem()) { 169 | //add highlight once 170 | if (!highlight && model.getItem().getHighlight()){ 171 | int highlightColor = tileItem.getHighlightColor(); 172 | itemBackground.setFillColor(highlightColor); 173 | entityModule.commitEntityState(0, itemBackground); 174 | highlight = true; 175 | } 176 | tooltipText += '\n' + model.getItem().toTooltip(); 177 | } 178 | tooltipModule.updateExtraTooltipText(group, tooltipText); 179 | } 180 | 181 | public void update(Observable observable, Object update) { 182 | super.update(model, update); 183 | if (update instanceof RemoveItemUpdate) 184 | removeItem(); 185 | else if (update instanceof ShowFrameUpdate) { 186 | showFrame = true; 187 | if(this.state == TileState.STILL) { 188 | this.state = TileState.MOVED; 189 | } 190 | } else if(update instanceof PushedUpdate) { 191 | this.state = TileState.PUSHED; 192 | this.tempDirection = ((PushedUpdate) update).getDirection(); 193 | }else if(update instanceof PoppedUpdate) { 194 | 195 | this.state = TileState.POPPED; 196 | this.tempDirection = ((PoppedUpdate) update).getDirection(); 197 | } 198 | } 199 | 200 | public Entity getEntity() { 201 | return group; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/TurnTextView.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import com.codingame.game.Referee; 4 | import com.codingame.game.Utils.Constants; 5 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 6 | import com.codingame.gameengine.module.entities.Text; 7 | 8 | public class TurnTextView extends AbstractView { 9 | private final int TURN_TEXT_POS_X = Constants.SCREEN_WIDTH - 490; 10 | private final int NUM_TURNS_TEXT_POS_X = Constants.SCREEN_WIDTH - 180; 11 | private final int TYPE_TEXT_POS_X = Constants.SCREEN_WIDTH - 160; 12 | private final int TURN_TEXT_POS_Y = 70; 13 | 14 | //import it once 15 | private final int maxTurns = Constants.MAX_GAME_TURNS; 16 | 17 | private Text turnText; 18 | private Text typeText; 19 | private Text numTurnsText; 20 | 21 | 22 | public TurnTextView(GraphicEntityModule entityModule){ 23 | super(entityModule); 24 | 25 | createTexts(); 26 | } 27 | 28 | private void createTexts() { 29 | turnText = entityModule.createText("") 30 | .setX(TURN_TEXT_POS_X) 31 | .setY(TURN_TEXT_POS_Y) 32 | .setZIndex(2) 33 | .setFontSize(50) 34 | .setFillColor(0xF0EFEF) 35 | .setFontFamily("Arial") 36 | //aligned to the left 37 | .setAnchorX(0) 38 | .setAnchorY(1); 39 | numTurnsText = entityModule.createText("") 40 | .setX(NUM_TURNS_TEXT_POS_X) 41 | .setY(TURN_TEXT_POS_Y) 42 | .setZIndex(2) 43 | .setFontSize(50) 44 | .setFillColor(0xF0EFEF) 45 | .setFontFamily("Arial") 46 | //aligned to the right 47 | .setAnchorX(1) 48 | .setAnchorY(1); 49 | typeText = entityModule.createText("") 50 | .setX(TYPE_TEXT_POS_X) 51 | .setY(TURN_TEXT_POS_Y) 52 | .setZIndex(2) 53 | .setFontSize(50) 54 | .setFillColor(0xF0EFEF) 55 | .setFontFamily("Arial") 56 | //aligned to the left 57 | .setAnchorX(0) 58 | .setAnchorY(1); 59 | } 60 | 61 | public void updateView() { 62 | if (Referee.turnType != null){ 63 | turnText.setText("Turn:"); 64 | typeText.setText(Referee.turnType.toString()); 65 | numTurnsText.setText(String.format("%d/%d", maxTurns - Referee.gameTurnsLeft, maxTurns)); 66 | } 67 | entityModule.commitEntityState(0, turnText, numTurnsText, typeText); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/View/ViewController.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.View; 2 | 3 | import com.codingame.game.Model.CardModel; 4 | import com.codingame.game.Model.TileModel; 5 | import com.codingame.game.Player; 6 | import com.codingame.game.Referee; 7 | import com.codingame.game.Utils.Constants; 8 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 9 | import com.codingame.view.tooltip.TooltipModule; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class ViewController { 15 | private List views = new ArrayList<>(); 16 | private GraphicEntityModule entityModule; 17 | private TooltipModule tooltipModule; 18 | 19 | private BoardView board; 20 | 21 | public ViewController(GraphicEntityModule entityModule, TooltipModule tooltipModule) { 22 | this.entityModule = entityModule; 23 | this.tooltipModule = tooltipModule; 24 | 25 | this.board = new BoardView(entityModule, tooltipModule); 26 | Referee.setObserver(board); 27 | 28 | initView(); 29 | } 30 | 31 | private void initView() { 32 | createBackground(); 33 | } 34 | 35 | public void update() { 36 | board.updateView(); 37 | 38 | for(AbstractView view : views) { 39 | view.updateView(); 40 | } 41 | disposeViews(); 42 | } 43 | 44 | private void disposeViews() { 45 | views.removeIf(view -> view.isDisposable()); 46 | } 47 | 48 | private void createBackground() { 49 | entityModule.createSprite() 50 | .setImage("background.jpg") 51 | .setBaseWidth(Constants.SCREEN_WIDTH) 52 | .setBaseHeight(Constants.SCREEN_HEIGHT) 53 | .setX(0) 54 | .setY(0) 55 | .setScale(1) 56 | .setAnchor(0) 57 | .setZIndex(-1); 58 | } 59 | 60 | public void createCardView(CardModel card) { 61 | views.add(board.createCardView(card)); 62 | } 63 | 64 | public void createCardDeckView(Player player) { 65 | CardDeckView deckView = new CardDeckView(entityModule, player.getPlayer()); 66 | views.add(deckView); 67 | } 68 | 69 | public void createTileView(TileModel tile) { 70 | views.add(board.createTileView(tile)); 71 | } 72 | 73 | public void createPlayerView(Player player) { 74 | //create view with player name and avatar 75 | views.add(new PlayerTextView(entityModule, player) ); 76 | views.add(board.createPlayerView(player)); 77 | } 78 | 79 | public void createTextView() { 80 | TurnTextView textView = new TurnTextView(entityModule); 81 | views.add(textView); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/endscreen/EndScreenModule.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view.endscreen; 2 | 3 | import com.codingame.gameengine.core.AbstractPlayer; 4 | import com.codingame.gameengine.core.GameManager; 5 | import com.codingame.gameengine.core.Module; 6 | import com.google.inject.Inject; 7 | 8 | public class EndScreenModule implements Module { 9 | 10 | private GameManager gameManager; 11 | private int[] scores; 12 | 13 | @Inject 14 | EndScreenModule(GameManager gameManager) { 15 | this.gameManager = gameManager; 16 | gameManager.registerModule(this); 17 | } 18 | 19 | public void setScores(int[] scores) { 20 | this.scores = scores; 21 | } 22 | 23 | @Override 24 | public final void onGameInit() { 25 | } 26 | @Override 27 | public final void onAfterGameTurn() { 28 | } 29 | 30 | @Override 31 | public final void onAfterOnEnd() { 32 | gameManager.setViewData("endScreen", scores); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/nicknameshandler/NicknamesHandlerModule.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view.nicknameshandler; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.codingame.game.Player; 7 | import com.codingame.gameengine.core.Module; 8 | import com.codingame.gameengine.core.MultiplayerGameManager; 9 | import com.codingame.gameengine.module.entities.Text; 10 | import com.google.inject.Inject; 11 | 12 | public class NicknamesHandlerModule implements Module { 13 | List nicknames = new ArrayList<>(2); 14 | private MultiplayerGameManager gameManager; 15 | 16 | @Inject 17 | public NicknamesHandlerModule(MultiplayerGameManager gameManager) { 18 | this.gameManager = gameManager; 19 | gameManager.registerModule(this); 20 | } 21 | 22 | @Override 23 | public void onGameInit() { 24 | gameManager.setViewGlobalData("nicks", nicknames.stream().mapToInt(n -> n.getId()).toArray()); 25 | } 26 | 27 | public void registerNickname(Text nick) { 28 | nicknames.add(nick); 29 | } 30 | 31 | @Override 32 | public void onAfterGameTurn() { 33 | 34 | } 35 | 36 | @Override 37 | public void onAfterOnEnd() { 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/tooltip/TooltipModule.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view.tooltip; 2 | 3 | import com.codingame.gameengine.core.AbstractPlayer; 4 | import com.codingame.gameengine.core.GameManager; 5 | import com.codingame.gameengine.core.Module; 6 | import com.codingame.gameengine.module.entities.Entity; 7 | import com.codingame.gameengine.module.entities.GraphicEntityModule; 8 | import com.google.inject.Inject; 9 | 10 | import java.util.Arrays; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | public class TooltipModule implements Module { 15 | 16 | GameManager gameManager; 17 | @Inject 18 | GraphicEntityModule entityModule; 19 | Map> registrations; 20 | Map> newRegistrations; 21 | Map extra, newExtra; 22 | 23 | @Inject 24 | TooltipModule(GameManager gameManager) { 25 | this.gameManager = gameManager; 26 | gameManager.registerModule(this); 27 | registrations = new HashMap<>(); 28 | newRegistrations = new HashMap<>(); 29 | extra = new HashMap<>(); 30 | newExtra = new HashMap<>(); 31 | } 32 | 33 | @Override 34 | public void onGameInit() { 35 | sendFrameData(); 36 | } 37 | 38 | @Override 39 | public void onAfterGameTurn() { 40 | sendFrameData(); 41 | } 42 | 43 | @Override 44 | public void onAfterOnEnd() { 45 | // Do nothing 46 | } 47 | 48 | private void sendFrameData() { 49 | Object[] data = { newRegistrations, newExtra }; 50 | gameManager.setViewData("tooltips", data); 51 | 52 | newRegistrations.clear(); 53 | newExtra.clear(); 54 | } 55 | 56 | public void registerEntity(Entity entity) { 57 | registerEntity(entity, new HashMap<>()); 58 | } 59 | 60 | public void registerEntity(Entity entity, Map params) { 61 | registerEntity(entity.getId(), params); 62 | } 63 | 64 | public void registerEntity(int id, Map params) { 65 | if (!params.equals(registrations.get(id))) { 66 | newRegistrations.put(id, params); 67 | registrations.put(id, params); 68 | } 69 | } 70 | 71 | boolean deepEquals(String[] a, String[] b) { 72 | return Arrays.deepEquals(a,b); 73 | } 74 | 75 | public Map getParams(int id) { 76 | Map params = registrations.get(id); 77 | if (params == null) { 78 | params = new HashMap<>(); 79 | registrations.put(id, params); 80 | } 81 | return params; 82 | } 83 | 84 | public void updateExtraTooltipText(Entity entity, String... lines) { 85 | int id = entity.getId(); 86 | if (!deepEquals(lines, extra.get(id))) { 87 | newExtra.put(id, lines); 88 | extra.put(id, lines); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/resources/view/assets/arrow_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/arrow_0.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/arrow_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/arrow_1.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/arrow_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/arrow_2.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/background.jpg -------------------------------------------------------------------------------- /src/main/resources/view/assets/background_name_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/background_name_0.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/background_name_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/background_name_1.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/cardBack_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/cardBack_0.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/cardBack_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/cardBack_1.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/cardFront_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/cardFront_0.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/cardFront_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/cardFront_1.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/elf_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/elf_0.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/elf_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/elf_1.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/frame.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/items_sheet.json: -------------------------------------------------------------------------------- 1 | {"frames":{"item_ARROW_0":{"frame":{"x":0,"y":0,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_ARROW_1":{"frame":{"x":0,"y":49,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_BOOK_0":{"frame":{"x":49,"y":0,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_BOOK_1":{"frame":{"x":49,"y":49,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_CANDY_0":{"frame":{"x":0,"y":98,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_CANDY_1":{"frame":{"x":49,"y":98,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_CANE_0":{"frame":{"x":98,"y":0,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_CANE_1":{"frame":{"x":98,"y":49,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_DIAMOND_0":{"frame":{"x":98,"y":98,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_DIAMOND_1":{"frame":{"x":0,"y":147,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_FISH_0":{"frame":{"x":49,"y":147,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_FISH_1":{"frame":{"x":98,"y":147,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_KEY_0":{"frame":{"x":147,"y":0,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_KEY_1":{"frame":{"x":147,"y":49,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_MASK_0":{"frame":{"x":147,"y":98,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_MASK_1":{"frame":{"x":147,"y":147,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_POTION_0":{"frame":{"x":0,"y":196,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_POTION_1":{"frame":{"x":49,"y":196,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_SCROLL_0":{"frame":{"x":98,"y":196,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_SCROLL_1":{"frame":{"x":147,"y":196,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_SHIELD_0":{"frame":{"x":196,"y":0,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_SHIELD_1":{"frame":{"x":196,"y":49,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_SWORD_0":{"frame":{"x":196,"y":98,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}},"item_SWORD_1":{"frame":{"x":196,"y":147,"w":48,"h":48},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":48,"h":48},"sourceSize":{"w":48,"h":48}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"items_sheet.png","size":{"w":244,"h":244},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/items_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/items_sheet.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/logo.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/tile_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/tile_background.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/tile_decorators.json: -------------------------------------------------------------------------------- 1 | {"frames":{"decors_0011":{"frame":{"x":0,"y":0,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"decors_0101":{"frame":{"x":0,"y":313,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"decors_0111":{"frame":{"x":313,"y":0,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"decors_1001":{"frame":{"x":313,"y":313,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"decors_1010":{"frame":{"x":0,"y":626,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"decors_1100":{"frame":{"x":313,"y":626,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"decors_1101":{"frame":{"x":626,"y":0,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"decors_1110":{"frame":{"x":626,"y":313,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"decors_1111":{"frame":{"x":626,"y":626,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"decors_0110":{"frame":{"x":0,"y":939,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"decors_1011":{"frame":{"x":313,"y":939,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"tile_decorators.png","size":{"w":938,"h":1251},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/tile_decorators.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/tile_decorators.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/tile_paths.json: -------------------------------------------------------------------------------- 1 | {"frames":{"paths_0011":{"frame":{"x":0,"y":0,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"paths_0101":{"frame":{"x":0,"y":313,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"paths_0110":{"frame":{"x":313,"y":0,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"paths_0111":{"frame":{"x":313,"y":313,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"paths_1001":{"frame":{"x":0,"y":626,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"paths_1010":{"frame":{"x":313,"y":626,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"paths_1011":{"frame":{"x":626,"y":0,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"paths_1100":{"frame":{"x":626,"y":313,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"paths_1101":{"frame":{"x":626,"y":626,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"paths_1110":{"frame":{"x":0,"y":939,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}},"paths_1111":{"frame":{"x":313,"y":939,"w":312,"h":312},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":312,"h":312},"sourceSize":{"w":312,"h":312}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"tile_paths.png","size":{"w":938,"h":1251},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/tile_paths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGameCommunity/XmasRush/df8eb80f709afaab446a4e3d5c30a73337b85e73/src/main/resources/view/assets/tile_paths.png -------------------------------------------------------------------------------- /src/main/resources/view/config.js: -------------------------------------------------------------------------------- 1 | import { GraphicEntityModule } from './entity-module/GraphicEntityModule.js'; 2 | import { EndScreenModule } from './modules/endScreen/EndScreenModule.js'; 3 | import { TooltipModule } from './modules/tooltip/TooltipModule.js'; 4 | import { NicknamesHandlerModule } from './modules/nicknamesHandlerModule/NicknamesHandlerModule.js'; 5 | import { options as viewOptions, ToggleModule } from './modules/toggleModule/ToggleModule.js'; 6 | 7 | // List of viewer modules that you want to use in your game 8 | export const modules = [ 9 | GraphicEntityModule, 10 | EndScreenModule, 11 | TooltipModule, 12 | ToggleModule, 13 | NicknamesHandlerModule 14 | ]; 15 | 16 | // Setting players' colors 17 | export const playerColors = [ 18 | '#ff1d5c', // player 1 19 | '#41a200' // player 2 20 | ]; 21 | 22 | export const options = [{ 23 | title: 'TILE SCENERY', 24 | get: function () { 25 | return viewOptions.decor 26 | }, 27 | set: function (value) { 28 | viewOptions.decor = value 29 | viewOptions.refreshDecor() 30 | }, 31 | values: { 32 | 'ON': true, 33 | 'OFF': false 34 | } 35 | }]; -------------------------------------------------------------------------------- /src/main/resources/view/modules/endScreen/EndScreenModule.js: -------------------------------------------------------------------------------- 1 | import {WIDTH, HEIGHT} from '../../core/constants.js' 2 | import {lerp, unlerp} from '../../core/utils.js' 3 | 4 | export class EndScreenModule { 5 | constructor (assets) { 6 | this.states = [] 7 | this.scores = [] 8 | this.globalData = {} 9 | window.module = this 10 | this.atEnd = false 11 | } 12 | 13 | static get name () { 14 | return 'endScreen' 15 | } 16 | 17 | updateScene (previousData, currentData, progress) { 18 | if (currentData.scores && progress === 1) { 19 | this.atEnd = true 20 | } else { 21 | this.atEnd = false 22 | } 23 | } 24 | 25 | handleFrameData (frameInfo, scores) { 26 | const state = { 27 | number: frameInfo.number, 28 | scores 29 | } 30 | if (scores) { 31 | this.scores = scores 32 | } 33 | this.states.push(state) 34 | return state 35 | } 36 | 37 | reinitScene (container, canvasData) { 38 | this.toDestroy = [] 39 | this.container = container 40 | this.endLayer = this.createEndScene(this) 41 | if (this.atEnd) { 42 | this.initEndScene() 43 | } 44 | this.container.addChild(this.endLayer) 45 | } 46 | 47 | animateScene (delta) { 48 | let step = Math.min(32, delta) 49 | 50 | if (this.atEnd) { 51 | if (!this.animationEnded) { 52 | this.renderEndScene(step) 53 | } 54 | } else { 55 | if (this.endTime > 0) { 56 | this.destroyEndScene() 57 | } 58 | this.endTime = 0 59 | } 60 | } 61 | 62 | destroyEndScene () { 63 | this.animationEnded = false 64 | this.endLayer.visible = false 65 | } 66 | 67 | initEndScene () { 68 | this.animationEnded = false 69 | this.endLayer.visible = true 70 | } 71 | 72 | renderEndScene (step) { 73 | var endOfEnd = 10000 74 | if (this.endTime === 0) { 75 | this.initEndScene() 76 | } 77 | 78 | var backS = 0 79 | var backD = 400 80 | var backP = unlerp(backS, backS + backD, this.endTime) 81 | this.endLayer.backgroundRanking.alpha = backP * 0.9 82 | 83 | var logoS = 400 84 | var logoD = 600 85 | var logoP = unlerp(logoS, logoS + logoD, this.endTime) 86 | this.endLayer.titleRanking.scale.x = this.endLayer.titleRanking.scale.y = 0.001 + lerp(10, 0.8, logoP) 87 | this.endLayer.titleRanking.visible = !!logoP 88 | 89 | var rankS = 1000 90 | var rankD = 300 91 | for (let i = 0; i < this.finishers.length; ++i) { 92 | var p = unlerp(rankS + rankD * i, rankS + rankD * i + rankD, this.endTime) 93 | this.finishers[i].alpha = p 94 | } 95 | 96 | this.endTime += step 97 | 98 | if (this.endTime >= endOfEnd) { 99 | this.animationEnded = true 100 | } 101 | } 102 | 103 | handleGlobalData (players, globalData) { 104 | this.globalData = { 105 | players: players, 106 | playerCount: players.length 107 | } 108 | } 109 | 110 | generateText (text, size, align, color, forceLato) { 111 | var textEl 112 | if (!forceLato) { 113 | textEl = new PIXI.extras.BitmapText(text, { 114 | font: size + 'px 04b', 115 | tint: color 116 | }) 117 | textEl.lineHeight = size 118 | } else { 119 | textEl = new PIXI.Text(text, { 120 | fontSize: Math.round(size / 1.2) + 'px', 121 | fontFamily: 'Lato', 122 | fontWeight: 'bold', 123 | fill: color 124 | }) 125 | textEl.lineHeight = Math.round(size / 1.2) 126 | this.toDestroy.push(textEl) 127 | } 128 | if (align === 'right') { 129 | textEl.anchor.x = 1 130 | } else if (align === 'center') { 131 | textEl.anchor.x = 0.5 132 | } 133 | return textEl 134 | } 135 | 136 | createFinisher (finisher) { 137 | var layer = new PIXI.Container() 138 | 139 | /** ************************************* */ 140 | var avatarContainer = new PIXI.Container() 141 | avatarContainer.y = 0 142 | avatarContainer.x = 0 143 | 144 | var backgroundAvatar = new PIXI.Graphics() 145 | backgroundAvatar.beginFill(0xffffff) 146 | backgroundAvatar.alpha = 0.1 147 | backgroundAvatar.drawRect(0, 0, 240, 120) 148 | avatarContainer.addChild(backgroundAvatar) 149 | 150 | var avatarBorder = new PIXI.Graphics() 151 | avatarBorder.lineStyle(1, 0xffffff) 152 | avatarBorder.alpha = 0.5 153 | avatarBorder.drawRect(0, 0, 120, 120) 154 | avatarContainer.addChild(avatarBorder) 155 | 156 | var avatar = new PIXI.Sprite(finisher.player.avatar) 157 | avatar.width = avatar.height = 120 158 | 159 | var rank = this.generateText(finisher.rank.toString(), 76, 'center', finisher.player.color, true) 160 | rank.anchor.y = 0.5 161 | rank.position.x = 160 162 | rank.position.y = 56 163 | avatarContainer.addChild(rank) 164 | 165 | var rankLetter = this.generateText(finisher.rank === 1 ? 'ST' : 'ND'.toString(), 34, 'left', finisher.player.color, true) 166 | rankLetter.position.x = 184 167 | rankLetter.position.y = 32 168 | avatarContainer.addChild(rankLetter) 169 | 170 | var hudAvatar = new PIXI.Container() 171 | hudAvatar.addChild(avatar) 172 | 173 | avatarContainer.addChild(hudAvatar) 174 | 175 | /** ************************************* */ 176 | 177 | var name = this.generateText(finisher.player.name.toUpperCase(), 50, 'left', finisher.player.color, true) 178 | var scoreLabel = this.generateText(((finisher.score >= 0) ? finisher.score.toString() + ' points' : '-'), 64, 'left', finisher.player.color, true) 179 | 180 | name.x = 330 181 | name.y = -4 182 | scoreLabel.x = 330 183 | scoreLabel.y = 50 184 | 185 | layer.addChild(avatarContainer) 186 | layer.addChild(name) 187 | layer.addChild(scoreLabel) 188 | 189 | return layer 190 | } 191 | 192 | createEndScene () { 193 | var layer = new PIXI.Container() 194 | 195 | var background = new PIXI.Graphics() 196 | background.beginFill(0, 0.85) 197 | background.drawRect(0, 0, WIDTH, HEIGHT) 198 | background.endFill() 199 | 200 | layer.backgroundRanking = background 201 | 202 | var titleRanking = new PIXI.Sprite.fromFrame('logo.png') 203 | titleRanking.anchor.x = titleRanking.anchor.y = 0.5 204 | layer.titleRanking = titleRanking 205 | 206 | titleRanking.position.x = WIDTH / 2 207 | titleRanking.position.y = 230 208 | 209 | var podium = [] 210 | for (var i = 0; i < this.globalData.playerCount; ++i) { 211 | podium.push({ 212 | score: this.scores[i], 213 | player: this.globalData.players[i], 214 | rank: 0 215 | }) 216 | } 217 | podium.sort(function (a, b) { 218 | return b.score - a.score 219 | }) 220 | 221 | this.finishers = [] 222 | var finishers = new PIXI.Container() 223 | var curRank = 1 224 | var elem 225 | for (i = 0; i < podium.length; ++i) { 226 | if (i > 0 && podium[i - 1].score !== podium[i].score) { 227 | curRank++ 228 | } 229 | 230 | podium[i].rank = curRank 231 | elem = this.createFinisher(podium[i]) 232 | finishers.addChild(elem) 233 | this.finishers.push(elem) 234 | } 235 | 236 | for (i = 0; i < this.finishers.length; ++i) { 237 | this.finishers[i].position.x = (WIDTH - this.finishers[0].width) / 2 238 | this.finishers[i].position.y = i * 150 239 | } 240 | finishers.y = 400 241 | 242 | layer.addChild(background) 243 | layer.addChild(titleRanking) 244 | layer.addChild(finishers) 245 | 246 | layer.visible = false 247 | return layer 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/main/resources/view/modules/nicknamesHandlerModule/NicknamesHandlerModule.js: -------------------------------------------------------------------------------- 1 | import { api as entityModule } from '../../entity-module/GraphicEntityModule.js' 2 | import { fitAspectRatio } from '../../core/utils.js' 3 | export const api = { 4 | showDamage: true, 5 | showCurve: false 6 | } 7 | 8 | export class NicknamesHandlerModule { 9 | constructor (assets) { 10 | this.mustShrinkNickname = true 11 | this.nicknameIds = [] 12 | } 13 | 14 | static get name () { 15 | return 'nicks' 16 | } 17 | 18 | updateScene (previousData, currentData, progress) { 19 | if (this.mustShrinkNickname) { 20 | this.mustShrinkNickname = false 21 | this.nicknameIds.forEach(entityId => { 22 | let entity = entityModule.entities.get(entityId) 23 | if (!entity.currentState.text || entity.currentState.text === '') { 24 | this.mustShrinkNickname = true 25 | } else { 26 | if (entity.graphics.width > 270) { 27 | let aspectRatio = fitAspectRatio(entity.graphics.width, entity.graphics.height, 270, 50) 28 | entity.graphics.scale.set(aspectRatio) 29 | } 30 | } 31 | }) 32 | } 33 | } 34 | 35 | handleFrameData (frameInfo, nothing) { 36 | return {...frameInfo} 37 | } 38 | 39 | reinitScene (container, canvasData) { 40 | this.mustShrinkNickname = true 41 | } 42 | 43 | animateScene (delta) { 44 | } 45 | 46 | handleGlobalData (players, nicknameIds) { 47 | this.nicknameIds = nicknameIds || [] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/view/modules/toggleModule/ToggleModule.js: -------------------------------------------------------------------------------- 1 | import { api as entityModule } from '../../entity-module/GraphicEntityModule.js' 2 | 3 | export const options = { 4 | decor: false, 5 | refreshDecor: () => {} 6 | } 7 | 8 | export class ToggleModule { 9 | constructor (assets) { 10 | this.interactive = {} 11 | this.previousFrame = { 12 | tooltips: {}, 13 | paths: {}, 14 | ownerships: {} 15 | } 16 | 17 | options.refreshDecor = () => { 18 | entityModule.entities.forEach( 19 | e => { 20 | if (e.currentState.image && e.currentState.image.indexOf('decor') >= 0) { 21 | e.graphics.visible = options.decor 22 | } 23 | }) 24 | } 25 | } 26 | 27 | static get name () { 28 | return 'togglemodule' 29 | } 30 | 31 | updateScene (previousData, currentData, progress) { 32 | this.currentFrame = currentData 33 | this.currentProgress = progress 34 | options.refreshDecor() 35 | } 36 | 37 | handleFrameData (frameInfo, data) { 38 | } 39 | 40 | reinitScene (container, canvasData) { 41 | } 42 | 43 | animateScene (delta) { 44 | } 45 | 46 | handleGlobalData (players, globalData) { 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/resources/view/modules/tooltip/TooltipModule.js: -------------------------------------------------------------------------------- 1 | import { ErrorLog } from '../../core/ErrorLog.js'; 2 | import { WIDTH, HEIGHT } from '../../core/constants.js'; 3 | import * as utils from '../../core/utils.js'; 4 | import { api as entityModule } from '../../entity-module/GraphicEntityModule.js'; 5 | 6 | 7 | function getMouseOverFunc(id, tooltip) { 8 | return function () { 9 | tooltip.inside[id] = true; 10 | }; 11 | } 12 | 13 | function getMouseOutFunc(id, tooltip) { 14 | return function () { 15 | delete tooltip.inside[id]; 16 | }; 17 | } 18 | 19 | function getEntityState(entity, frame, progress) { 20 | const subStates = entity.states[frame]; 21 | if (subStates && subStates.length) { 22 | return subStates[subStates.length - 1]; 23 | } 24 | return null; 25 | } 26 | 27 | function getMouseMoveFunc(tooltip, container, module) { 28 | return function (ev) { 29 | if (tooltip) { 30 | var pos = ev.data.getLocalPosition(container); 31 | tooltip.x = pos.x; 32 | tooltip.y = pos.y; 33 | var point = { 34 | x: pos.x * entityModule.coeff, 35 | y: pos.y * entityModule.coeff 36 | }; 37 | 38 | const showing = []; 39 | const ids = Object.keys(tooltip.inside).map(n => +n); 40 | 41 | for (let id of ids) { 42 | if (tooltip.inside[id]) { 43 | const entity = entityModule.entities.get(id); 44 | const state = entity && getEntityState(entity, module.currentFrame.number); 45 | if (!state || state.alpha === 0 || !state.visible) { 46 | delete tooltip.inside[id]; 47 | } else { 48 | showing.push(id); 49 | } 50 | } 51 | } 52 | 53 | if (showing.length) { 54 | const tooltipBlocks = []; 55 | for (let show of showing) { 56 | const entity = entityModule.entities.get(show); 57 | const state = getEntityState(entity, module.currentFrame.number); 58 | if (state !== null) { 59 | let tooltipBlock; 60 | const params = module.currentFrame.registered[show]; 61 | 62 | tooltip.visible = true; 63 | const extra = module.currentFrame.extraText[show]; 64 | if (extra && extra.length) { 65 | tooltipBlock = extra; 66 | } 67 | tooltipBlocks.push(tooltipBlock); 68 | } 69 | } 70 | tooltip.label.text = tooltipBlocks.join('\n──────────\n') 71 | } else { 72 | tooltip.visible = false; 73 | } 74 | 75 | tooltip.background.width = tooltip.label.width + 20; 76 | tooltip.background.height = tooltip.label.height + 20; 77 | 78 | tooltip.pivot.x = -30; 79 | tooltip.pivot.y = -50; 80 | 81 | if (tooltip.y - tooltip.pivot.y + tooltip.height > HEIGHT) { 82 | tooltip.pivot.y = 10 + tooltip.height; 83 | tooltip.y -= tooltip.y - tooltip.pivot.y + tooltip.height - HEIGHT 84 | } 85 | 86 | if (tooltip.x - tooltip.pivot.x + tooltip.width > WIDTH) { 87 | tooltip.pivot.x = tooltip.width; 88 | } 89 | } 90 | 91 | } 92 | }; 93 | 94 | export class TooltipModule { 95 | constructor(assets) { 96 | this.interactive = {}; 97 | this.previousFrame = { 98 | registrations: {}, 99 | extra: {} 100 | }; 101 | this.lastProgress = 1; 102 | this.lastFrame = 0; 103 | 104 | } 105 | 106 | static get name() { 107 | return 'tooltips'; 108 | } 109 | 110 | updateScene(previousData, currentData, progress) { 111 | this.currentFrame = currentData; 112 | this.currentProgress = progress; 113 | } 114 | 115 | handleFrameData(frameInfo, [registrations, extra]) { 116 | const registered = { ...registrations }; 117 | const extraText = { ...this.previousFrame.extraText, ...extra }; 118 | 119 | Object.keys(registrations).forEach( 120 | k => { 121 | this.interactive[k] = true; 122 | } 123 | ); 124 | 125 | const frame = { registered, extraText, number: frameInfo.number }; 126 | this.previousFrame = frame; 127 | return frame; 128 | } 129 | 130 | reinitScene(container, canvasData) { 131 | this.tooltip = this.initTooltip(); 132 | entityModule.entities.forEach(entity => { 133 | if (this.interactive[entity.id]) { 134 | entity.container.interactive = true; 135 | entity.container.mouseover = getMouseOverFunc(entity.id, this.tooltip); 136 | entity.container.mouseout = getMouseOutFunc(entity.id, this.tooltip); 137 | } 138 | }); 139 | this.container = container; 140 | container.interactive = true; 141 | container.mousemove = getMouseMoveFunc(this.tooltip, container, this); 142 | container.addChild(this.tooltip); 143 | } 144 | 145 | generateText(text, size, color, align) { 146 | var textEl = new PIXI.Text(text, { 147 | fontSize: Math.round(size / 1.2) + 'px', 148 | fontFamily: 'monospace', 149 | fontWeight: 'bold', 150 | fill: color 151 | }); 152 | 153 | textEl.lineHeight = Math.round(size / 1.2); 154 | if (align === 'right') { 155 | textEl.anchor.x = 1; 156 | } else if (align === 'center') { 157 | textEl.anchor.x = 0.5; 158 | } 159 | 160 | return textEl; 161 | }; 162 | 163 | initTooltip() { 164 | var tooltip = new PIXI.Container(); 165 | var background = tooltip.background = new PIXI.Graphics(); 166 | var label = tooltip.label = this.generateText('', 36, 0xFFFFFF, 'left'); 167 | 168 | background.beginFill(0x0, 0.7); 169 | background.drawRect(0, 0, 200, 185); 170 | background.endFill(); 171 | background.x = -10; 172 | background.y = -10; 173 | 174 | tooltip.visible = false; 175 | tooltip.inside = {}; 176 | 177 | tooltip.addChild(background); 178 | tooltip.addChild(label); 179 | 180 | tooltip.interactiveChildren = false; 181 | return tooltip; 182 | }; 183 | 184 | animateScene(delta) { 185 | 186 | } 187 | 188 | handleGlobalData(players, globalData) { 189 | 190 | } 191 | } 192 | 193 | class NotYetImplemented extends Error { 194 | constructor(feature) { 195 | super('Not yet implemented: "' + feature); 196 | this.feature = feature; 197 | this.name = 'NotYetImplemented'; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/test/java/Main.java: -------------------------------------------------------------------------------- 1 | import com.codingame.gameengine.runner.MultiplayerGameRunner; 2 | 3 | public class Main { 4 | public static void main(String[] args) { 5 | 6 | final String AVATAR_TWINKLE = "https://static.codingame.com/servlet/fileservlet?id=23672527342224&format=profile_avatar"; 7 | final String AVATAR_PIXIE = "https://static.codingame.com/servlet/fileservlet?id=23672566492606&format=profile_avatar"; 8 | 9 | MultiplayerGameRunner gameRunner = new MultiplayerGameRunner(); 10 | 11 | gameRunner.addAgent(Player1.class, "Twinkle", AVATAR_TWINKLE); 12 | gameRunner.addAgent(Player2.class, "Pixie", AVATAR_PIXIE); 13 | 14 | System.setProperty("league.level", "3"); 15 | gameRunner.setSeed(20L); 16 | 17 | gameRunner.start(); 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/Player1.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | public class Player1 { 4 | public static void main(String args[]) { 5 | Scanner in = new Scanner(System.in); 6 | int boardWidth = 7; 7 | int boardHeight = 7; 8 | 9 | int turn = 0; 10 | // game loop 11 | while (true) { 12 | 13 | int turnType = in.nextInt(); 14 | for (int i = 0; i < boardHeight; i++) { 15 | for (int j = 0; j < boardWidth; j++) { 16 | String tile = in.next(); 17 | System.err.print(tile + " "); 18 | } 19 | System.err.println(); 20 | } 21 | for (int i = 0; i < 2; i++) { 22 | int numPlayerCards = in.nextInt(); // the number of cards in the stack for each player 23 | int playerX = in.nextInt(); 24 | int playerY = in.nextInt(); 25 | String playerTile = in.next(); 26 | System.err.println(numPlayerCards + " " + playerX + "," + playerY + " " + playerTile); 27 | } 28 | int numItems = in.nextInt(); // the total number of items available on board and on player tiles (does not include quest cards) 29 | for (int i = 0; i < numItems; i++) { 30 | String itemName = in.next(); 31 | int itemX = in.nextInt(); 32 | int itemY = in.nextInt(); 33 | int itemPlayerId = in.nextInt(); 34 | System.err.println(itemName + itemPlayerId + " " + itemX + "," + itemY); 35 | } 36 | int numQuests = in.nextInt(); // the total number of available quest cards for both players 37 | for (int i = 0; i < numQuests; i++) { 38 | String questItemName = in.next(); 39 | int questPlayerId = in.nextInt(); 40 | System.err.println(questItemName + questPlayerId); 41 | } 42 | 43 | //league 3 44 | if (turn == 0) System.out.println("PUSH 0 DOWN"); 45 | if (turn == 1) System.out.println("MOVE RIGHT UP RIGHT RIGHT DOWN"); 46 | if (turn == 2) System.out.println("PUSH 1 RIGHT"); 47 | if (turn == 3) System.out.println("MOVE UP RIGHT RIGHT LEFT LEFT DOWN"); 48 | if (turn == 4) System.out.println("PUSH 6 UP"); 49 | if (turn == 5) System.out.println("MOVE DOWN DOWN DOWN DOWN LEFT DOWN RIGHT RIGHT LEFT LEFT UP RIGHT UP UP UP UP UP RIGHT RIGHT"); 50 | if (turn == 6) System.out.println("PUSH 4 RIGHT"); 51 | if (turn == 7) System.out.println("PASS"); 52 | if (turn == 8) System.out.println("PUSH 6 DOWN"); 53 | if (turn == 9) System.out.println("MOVE UP"); 54 | if (turn == 10) System.out.println("PUSH 5 UP"); 55 | if (turn == 11) System.out.println("PASS"); 56 | if (turn == 12) System.out.println("PUSH 4 LEFT"); 57 | if (turn == 13) System.out.println("PASS"); 58 | if (turn == 14) System.out.println("PUSH 0 LEFT"); 59 | if (turn == 15) System.out.println("MOVE RIGHT"); 60 | if (turn == 16) System.out.println("PUSH 0 RIGHT"); 61 | if (turn == 17) System.out.println("MOVE RIGHT"); 62 | if (turn == 18) System.out.println("PUSH 0 RIGHT"); 63 | if (turn == 19) System.out.println("MOVE DOWN RIGHT UP RIGHT DOWN DOWN"); 64 | if (turn == 20) System.out.println("PUSH 2 RIGHT"); 65 | if (turn == 21) System.out.println("MOVE DOWN DOWN UP UP"); 66 | if (turn == 22) System.out.println("PUSH 2 LEFT"); 67 | if (turn == 23) System.out.println("MOVE UP UP LEFT DOWN"); 68 | turn++; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/Player2.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | public class Player2 { 4 | public static void main(String args[]) { 5 | Scanner in = new Scanner(System.in); 6 | int boardWidth = 7; 7 | int boardHeight = 7; 8 | 9 | int turn = 0; 10 | // game loop 11 | while (true) { 12 | int turnType = in.nextInt(); 13 | for (int i = 0; i < boardHeight; i++) { 14 | for (int j = 0; j < boardWidth; j++) { 15 | String tile = in.next(); 16 | System.err.print(tile + " "); 17 | } 18 | System.err.println(); 19 | } 20 | for (int i = 0; i < 2; i++) { 21 | int numPlayerCards = in.nextInt(); // the number of cards in the stack for each player 22 | int playerX = in.nextInt(); 23 | int playerY = in.nextInt(); 24 | String playerTile = in.next(); 25 | System.err.println(numPlayerCards + " " + playerX + "," + playerY + " " + playerTile); 26 | } 27 | int numItems = in.nextInt(); // the total number of items available on board and on player tiles (does not include quest cards) 28 | for (int i = 0; i < numItems; i++) { 29 | String itemName = in.next(); 30 | int itemX = in.nextInt(); 31 | int itemY = in.nextInt(); 32 | int itemPlayerId = in.nextInt(); 33 | System.err.println(itemName + itemPlayerId + " " + itemX + "," + itemY); 34 | } 35 | int numQuests = in.nextInt(); // the total number of available quest cards for both players 36 | for (int i = 0; i < numQuests; i++) { 37 | String questItemName = in.next(); 38 | int questPlayerId = in.nextInt(); 39 | System.err.println(questItemName + questPlayerId); 40 | } 41 | 42 | //league 3 43 | if (turn == 0) System.out.println("PUSH 1 UP"); 44 | if (turn == 1) System.out.println("MOVE UP"); 45 | if (turn == 2) System.out.println("PUSH 5 UP"); 46 | if (turn == 3) System.out.println("MOVE DOWN LEFT"); 47 | if (turn == 4) System.out.println("PUSH 5 DOWN"); 48 | if (turn == 5) System.out.println("MOVE LEFT DOWN DOWN DOWN DOWN DOWN LEFT"); 49 | if (turn == 6) System.out.println("PUSH 5 LEFT"); 50 | if (turn == 7) System.out.println("MOVE UP DOWN DOWN"); 51 | if (turn == 8) System.out.println("PUSH 2 DOWN"); 52 | if (turn == 9) System.out.println("MOVE RIGHT DOWN RIGHT DOWN UP UP RIGHT"); 53 | if (turn == 10) System.out.println("PUSH 0 UP"); 54 | if (turn == 11) System.out.println("MOVE LEFT"); 55 | if (turn == 12) System.out.println("PUSH 1 LEFT"); 56 | if (turn == 13) System.out.println("PASS"); 57 | if (turn == 14) System.out.println("PUSH 4 UP"); 58 | if (turn == 15) System.out.println("MOVE DOWN UP RIGHT UP UP UP RIGHT"); 59 | if (turn == 16) System.out.println("PUSH 2 RIGHT"); 60 | if (turn == 17) System.out.println("MOVE UP RIGHT UP LEFT"); 61 | if (turn == 18) System.out.println("PUSH 2 LEFT"); 62 | if (turn == 19) System.out.println("MOVE DOWN LEFT DOWN UP RIGHT UP RIGHT"); 63 | if (turn == 20) System.out.println("PUSH 2 UP"); 64 | if (turn == 21) System.out.println("MOVE UP RIGHT DOWN RIGHT UP RIGHT UP UP DOWN DOWN LEFT DOWN LEFT UP LEFT"); 65 | if (turn == 22) System.out.println("PUSH 5 LEFT"); 66 | if (turn == 23) System.out.println("MOVE UP"); 67 | turn++; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/com/codingame/game/InputActions/MoveActionTest.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.InputActions; 2 | 3 | import com.codingame.game.Utils.Constants; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | public class MoveActionTest { 12 | 13 | @Test(expected=AssertionError.class) 14 | public void testAssertionsEnabled() { 15 | assert false; 16 | } 17 | 18 | @Test 19 | public void testConstructorMutatorsObservers() { 20 | Action.Type type = Action.Type.MOVE; 21 | Constants.Direction step1 = Constants.Direction.UP; 22 | Constants.Direction step2 = Constants.Direction.LEFT; 23 | MoveAction action = new MoveAction(type); 24 | assertEquals(type, action.getType()); 25 | assertEquals(null, action.getStep()); 26 | action.addStep(step1); 27 | action.addStep(step2); 28 | assertEquals(step1, action.getStep()); 29 | assertEquals(step2, action.getStep()); 30 | assertEquals(null, action.getStep()); 31 | } 32 | 33 | @Test 34 | public void testIsEmptySetEmpty() { 35 | Action.Type type = Action.Type.MOVE; 36 | Constants.Direction step1 = Constants.Direction.UP; 37 | MoveAction action = new MoveAction(type); 38 | assertTrue(action.isEmpty()); 39 | action.addStep(step1); 40 | assertFalse(action.isEmpty()); 41 | action.setEmpty(); 42 | assertTrue(action.isEmpty()); 43 | } 44 | 45 | @Test 46 | public void testActionMethods() { 47 | Action.Type type = Action.Type.MOVE; 48 | MoveAction action = new MoveAction(type); 49 | Action.Type turn = Action.Type.MOVE; 50 | assertFalse(action.isPassAction()); 51 | assertTrue(action.isLegalAction(turn)); 52 | turn = Action.Type.PUSH; 53 | assertFalse(action.isLegalAction(turn)); 54 | assertEquals(1, action.getType().getValue()); 55 | } 56 | } -------------------------------------------------------------------------------- /src/test/java/com/codingame/game/InputActions/PassActionTest.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.InputActions; 2 | 3 | import com.codingame.game.Utils.Constants; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.*; 7 | 8 | public class PassActionTest { 9 | 10 | @Test(expected=AssertionError.class) 11 | public void testAssertionsEnabled() { 12 | assert false; 13 | } 14 | 15 | @Test 16 | public void testConstructorObservers() { 17 | Action.Type type = Action.Type.PASS; 18 | PassAction action = new PassAction(type); 19 | assertEquals(type, action.getType()); 20 | assertTrue(action.isPassAction()); 21 | } 22 | 23 | @Test 24 | public void testLegalAction() { 25 | Action.Type type = Action.Type.PASS; 26 | PassAction action = new PassAction(type); 27 | Action.Type turn = Action.Type.PUSH; 28 | assertFalse(action.isLegalAction(turn)); 29 | turn = Action.Type.MOVE; 30 | assertTrue(action.isLegalAction(turn)); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/test/java/com/codingame/game/InputActions/PushActionTest.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.InputActions; 2 | 3 | import com.codingame.game.Utils.Constants; 4 | import org.junit.Test; 5 | 6 | import java.util.Arrays; 7 | 8 | import static org.junit.Assert.*; 9 | 10 | public class PushActionTest { 11 | 12 | @Test(expected=AssertionError.class) 13 | public void testAssertionsEnabled() { 14 | assert false; 15 | } 16 | 17 | @Test(expected=AssertionError.class) 18 | public void testIncorrectLine() throws AssertionError { 19 | int lineId = Constants.MAP_SIZE + 1; 20 | Constants.Direction direction = Constants.Direction.UP; 21 | Action.Type type = Action.Type.PUSH; 22 | PushAction action = new PushAction(lineId, direction, type); 23 | } 24 | 25 | @Test(expected=AssertionError.class) 26 | public void testPushSameLineLargerActionsSize() throws AssertionError { 27 | int lineId = 1; 28 | Constants.Direction direction1 = Constants.Direction.UP; 29 | Constants.Direction direction2 = Constants.Direction.DOWN; 30 | Action.Type type = Action.Type.PUSH; 31 | 32 | PushAction action1 = new PushAction(lineId, direction1, type); 33 | PushAction action2 = new PushAction(lineId, direction2, type); 34 | PushAction action3 = new PushAction(lineId, direction1, type); 35 | 36 | PushAction.pushSameLine(Arrays.asList(action1, action2, action3)); 37 | } 38 | 39 | @Test(expected=AssertionError.class) 40 | public void testPushSameLineSmallerActionsSize() throws AssertionError { 41 | int lineId = 1; 42 | Constants.Direction direction1 = Constants.Direction.UP; 43 | Action.Type type = Action.Type.PUSH; 44 | 45 | PushAction action1 = new PushAction(lineId, direction1, type); 46 | 47 | PushAction.pushSameLine(Arrays.asList(action1)); 48 | } 49 | 50 | @Test(expected=AssertionError.class) 51 | public void testPushSameLineMixedPlanes() throws AssertionError { 52 | int lineId = 1; 53 | Constants.Direction direction1 = Constants.Direction.UP; 54 | Constants.Direction direction2 = Constants.Direction.LEFT; 55 | Action.Type type = Action.Type.PUSH; 56 | 57 | PushAction action1 = new PushAction(lineId, direction1, type); 58 | PushAction action2 = new PushAction(lineId, direction2, type); 59 | 60 | PushAction.pushSameLine(Arrays.asList(action1, action2)); 61 | } 62 | 63 | @Test 64 | public void testConstructorObservers() { 65 | int lineId = 5; 66 | Constants.Direction direction = Constants.Direction.UP; 67 | Action.Type type = Action.Type.PUSH; 68 | PushAction action = new PushAction(lineId, direction, type); 69 | assertEquals(type, action.getType()); 70 | assertEquals(lineId, action.getLine()); 71 | assertEquals(direction, action.getDirection()); 72 | assertFalse(action.isHorizontal()); 73 | } 74 | 75 | @Test 76 | public void testActionMethods() { 77 | int lineId = 5; 78 | Constants.Direction direction = Constants.Direction.UP; 79 | Action.Type type = Action.Type.PUSH; 80 | Action.Type turn = Action.Type.PUSH; 81 | PushAction action = new PushAction(lineId, direction, type); 82 | assertFalse(action.isPassAction()); 83 | assertTrue(action.isLegalAction(turn)); 84 | turn = Action.Type.MOVE; 85 | assertFalse(action.isLegalAction(turn)); 86 | assertEquals(0, action.getType().getValue()); 87 | } 88 | 89 | @Test 90 | public void testPushSameLine() { 91 | int lineId1 = 5; 92 | int lineId2 = 1; 93 | Constants.Direction direction1 = Constants.Direction.UP; 94 | Constants.Direction direction2 = Constants.Direction.DOWN; 95 | Action.Type type = Action.Type.PUSH; 96 | 97 | PushAction action1 = new PushAction(lineId1, direction1, type); 98 | PushAction action2 = new PushAction(lineId1, direction1, type); 99 | assertTrue(PushAction.pushSameLine(Arrays.asList(action1, action2))); 100 | 101 | PushAction action3 = new PushAction(lineId1, direction2, type); 102 | assertTrue(PushAction.pushSameLine(Arrays.asList(action1, action3))); 103 | 104 | PushAction action4 = new PushAction(lineId2, direction1, type); 105 | assertFalse(PushAction.pushSameLine(Arrays.asList(action1, action4))); 106 | } 107 | 108 | @Test 109 | public void testToString() { 110 | int lineId1 = 0; 111 | int lineId2 = 2; 112 | int lineId3 = 5; 113 | int lineId4 = 6; 114 | Constants.Direction direction1 = Constants.Direction.UP; 115 | Constants.Direction direction2 = Constants.Direction.RIGHT; 116 | Constants.Direction direction3 = Constants.Direction.DOWN; 117 | Constants.Direction direction4 = Constants.Direction.LEFT; 118 | Action.Type type = Action.Type.PUSH; 119 | 120 | PushAction action1 = new PushAction(lineId1, direction1, type); 121 | PushAction action2 = new PushAction(lineId2, direction2, type); 122 | PushAction action3 = new PushAction(lineId3, direction3, type); 123 | PushAction action4 = new PushAction(lineId4, direction4, type); 124 | 125 | assertEquals("00", action1.toString()); 126 | assertEquals("21", action2.toString()); 127 | assertEquals("52", action3.toString()); 128 | assertEquals("63", action4.toString()); 129 | 130 | } 131 | 132 | } -------------------------------------------------------------------------------- /src/test/java/com/codingame/game/Model/CardModelTest.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import com.codingame.game.Utils.Vector2; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.*; 7 | 8 | 9 | public class CardModelTest { 10 | 11 | @Test(expected=AssertionError.class) 12 | public void testAssertionsEnabled() { 13 | assert false; 14 | } 15 | 16 | @Test(expected=AssertionError.class) 17 | public void testIncorrectItem() throws AssertionError { 18 | CardModel card = new CardModel(new Item("APPLE", 3), 19 | new Vector2(2, 1)); 20 | } 21 | 22 | @Test 23 | public void testConstructorObservers() { 24 | Item item = new Item("BOOK", 1); 25 | Vector2 pos1 = new Vector2(2, 1); 26 | Vector2 pos2 = new Vector2(1, 3); 27 | CardModel card = new CardModel(item, pos1); 28 | assertTrue(card.getItem().equals(item)); 29 | assertTrue(card.getPos().equals(pos1)); 30 | card.setPos(pos2); 31 | assertTrue(card.getPos().equals(pos2)); 32 | } 33 | 34 | @Test 35 | public void testConstructorExposure() { 36 | Item item = new Item("BOOK", 1); 37 | Vector2 pos = new Vector2(2, 1); 38 | CardModel card = new CardModel(item, pos); 39 | item = new Item("ARROW", 0); 40 | pos = new Vector2(1, 3); 41 | assertFalse(card.getItem().equals(item)); 42 | assertFalse(card.getPos().equals(pos)); 43 | } 44 | 45 | @Test 46 | public void testGetterExposure() { 47 | Item item1 = new Item("BOOK", 1); 48 | Vector2 pos1 = new Vector2(2, 1); 49 | CardModel card = new CardModel(item1, pos1); 50 | Item item2 = card.getItem(); 51 | Vector2 pos2 = card.getPos(); 52 | item2 = new Item("ARROW", 0); 53 | pos2 = new Vector2(1, 3); 54 | assertFalse(card.getItem().equals(item2)); 55 | assertFalse(card.getPos().equals(pos2)); 56 | assertTrue(card.getItem().equals(item1)); 57 | assertTrue(card.getPos().equals(pos1)); 58 | } 59 | 60 | @Test 61 | public void testCardToString() { 62 | Item item = new Item("BOOK", 1); 63 | Vector2 pos = new Vector2(2, 1); 64 | CardModel card = new CardModel(item, pos); 65 | assertEquals("BOOK 1", card.cardToString()); 66 | } 67 | 68 | @Test 69 | public void testOpponentCardToString() { 70 | Item item = new Item("BOOK", 1); 71 | Vector2 pos = new Vector2(2, 1); 72 | CardModel card = new CardModel(item, pos); 73 | assertEquals("BOOK 0", card.opponentCardToString()); 74 | } 75 | 76 | @Test 77 | public void testFlip() { 78 | Item item = new Item("BOOK", 1); 79 | Vector2 pos = new Vector2(2, 1); 80 | CardModel card = new CardModel(item, pos); 81 | assertFalse(item.getHighlight()); 82 | card.flip(); 83 | assertTrue(item.getHighlight()); 84 | card.flip(); 85 | assertTrue(item.getHighlight()); 86 | } 87 | } -------------------------------------------------------------------------------- /src/test/java/com/codingame/game/Model/ItemTest.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import org.junit.Test; 4 | import static org.junit.Assert.*; 5 | 6 | import java.util.*; 7 | 8 | public class ItemTest { 9 | 10 | @Test(expected=AssertionError.class) 11 | public void testAssertionsEnabled() { 12 | assert false; 13 | } 14 | 15 | @Test(expected=AssertionError.class) 16 | public void testItemIncorrectName() throws AssertionError { 17 | Item item = new Item("APPLE", 0); 18 | } 19 | 20 | @Test(expected=AssertionError.class) 21 | public void testItemIncorrectId() throws AssertionError { 22 | Item item = new Item("BOOK", 3); 23 | } 24 | 25 | @Test 26 | public void testItemObservers() { 27 | Item item = new Item("BOOK", 0); 28 | assertEquals("BOOK", item.getName()); 29 | assertEquals(0, item.getPlayerId()); 30 | } 31 | 32 | @Test 33 | public void testTwoItemsDifferentName() { 34 | Set items = new HashSet<>(); 35 | Item item1 = new Item("BOOK", 1); 36 | Item item2 = new Item("POTION", 1); 37 | items.add(item1); 38 | items.add(item2); 39 | assertTrue(items.size() == 2); 40 | } 41 | 42 | @Test 43 | public void testTwoItemsDifferentIds() { 44 | Set items = new HashSet<>(); 45 | Item item1 = new Item("BOOK", 0); 46 | Item item2 = new Item("BOOK", 1); 47 | items.add(item1); 48 | items.add(item2); 49 | assertTrue(items.size() == 2); 50 | } 51 | 52 | @Test 53 | public void testTwoSameItems() { 54 | Set items = new HashSet<>(); 55 | Item item1 = new Item("BOOK", 0); 56 | Item item2 = new Item("BOOK", 0); 57 | items.add(item1); 58 | items.add(item2); 59 | assertTrue(items.size() == 1); 60 | } 61 | 62 | @Test 63 | public void testRefExposure() { 64 | Item item1 = new Item("BOOK", 0); 65 | Item item2 = item1; 66 | item1 = new Item("DIAMOND", 1); 67 | assertTrue(item2.getName().equals("BOOK")); 68 | assertTrue(item2.getPlayerId() == 0); 69 | } 70 | 71 | @Test 72 | public void testItemToString() { 73 | Item item = new Item("BOOK", 1); 74 | assertEquals("BOOK 1", item.itemToString()); 75 | } 76 | 77 | @Test 78 | public void testGetOpponentsId() { 79 | Item item = new Item("BOOK", 1); 80 | assertEquals(0, item.getOpponentId()); 81 | } 82 | 83 | @Test 84 | public void testOpponentVectorToString() { 85 | Item item = new Item("BOOK", 1); 86 | assertEquals("BOOK 0", item.opponentItemToString()); 87 | } 88 | 89 | @Test 90 | public void testSetGetHighlight() { 91 | Item item = new Item("BOOK", 1); 92 | assertFalse(item.getHighlight()); 93 | item.setHighlight(); 94 | assertTrue(item.getHighlight()); 95 | item.setHighlight(); 96 | assertTrue(item.getHighlight()); 97 | } 98 | } -------------------------------------------------------------------------------- /src/test/java/com/codingame/game/Model/PlayerModelTest.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertNull; 6 | import static org.junit.Assert.assertTrue; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import org.junit.Test; 13 | 14 | import com.codingame.game.Utils.Constants; 15 | import com.codingame.game.Utils.Vector2; 16 | 17 | public class PlayerModelTest { 18 | 19 | @Test(expected=AssertionError.class) 20 | public void testAssertionsEnabled() { 21 | assert false; 22 | } 23 | 24 | @Test(expected=AssertionError.class) 25 | public void testSetCardsIncorrectId() throws AssertionError { 26 | int id = 1; 27 | PlayerModel player = new PlayerModel(id); 28 | Item item1 = new Item("BOOK", id); 29 | Item item2 = new Item("POTION", 0); 30 | List itemList = new ArrayList<>(); 31 | itemList.addAll(Arrays.asList(item1, item2)); 32 | player.setCards(itemList); 33 | } 34 | 35 | @Test(expected=AssertionError.class) 36 | public void testSetCardsRepeatedItems() throws AssertionError { 37 | int id = 1; 38 | PlayerModel player = new PlayerModel(id); 39 | Item item1 = new Item("BOOK", id); 40 | Item item2 = new Item("BOOK", id); 41 | List itemList = new ArrayList<>(); 42 | itemList.addAll(Arrays.asList(item1, item2)); 43 | player.setCards(itemList); 44 | } 45 | 46 | @Test 47 | public void testConstructor() { 48 | int id = 1; 49 | PlayerModel player = new PlayerModel(id); 50 | assertEquals(id, player.id); 51 | assertEquals(Constants.PLAYER_POSITIONS.get(id), player.getPos()); 52 | } 53 | 54 | @Test 55 | public void testTileMethods() { 56 | int id = 0; 57 | PlayerModel player = new PlayerModel(id); 58 | Vector2 pos = new Vector2(1, 2); 59 | TileModel tile1 = new TileModel("1111", pos); 60 | TileModel tile2 = new TileModel("1001", pos); 61 | assertNull(tile1.getPlayerId()); 62 | player.setTile(tile1); 63 | assertTrue(tile1.getPlayerId() == id); 64 | assertEquals(tile1, player.getTile()); 65 | assertEquals(Constants.TILE_MODEL_POSITIONS.get(id), player.getTile().getPos()); 66 | player.setTile(tile2); 67 | assertNull(tile1.getPlayerId()); 68 | } 69 | 70 | @Test 71 | public void testSetGetCards() { 72 | int id = 1; 73 | PlayerModel player = new PlayerModel(id); 74 | Item item1 = new Item("BOOK", id); 75 | Item item2 = new Item("POTION", id); 76 | List itemList = new ArrayList<>(); 77 | itemList.addAll(Arrays.asList(item1, item2)); 78 | player.setCards(itemList); 79 | assertEquals(2, player.getCards().size()); 80 | assertEquals(item1, player.getCards().get(0).getItem()); 81 | assertEquals(item2, player.getCards().get(1).getItem()); 82 | List cards = player.getCards(); 83 | cards = new ArrayList<>(); 84 | assertFalse(player.getCards().isEmpty()); 85 | } 86 | 87 | @Test 88 | public void testRemoveItemCard() { 89 | int id = 1; 90 | PlayerModel player = new PlayerModel(id); 91 | Item item1 = new Item("BOOK", id); 92 | Item item2 = new Item("POTION", id); 93 | int numVisibleCards = 1; 94 | List itemList = Arrays.asList(item1, item2); 95 | player.setCards(itemList); 96 | assertFalse(player.removeCard(item1)); 97 | assertFalse(player.removeCard(item2)); 98 | player.setNumVisibleCards(numVisibleCards); 99 | player.flipCards(); 100 | assertTrue(player.removeCard(item2)); 101 | assertFalse(player.removeCard(item2)); 102 | player.flipCards(); 103 | assertTrue(player.removeCard(item1)); 104 | assertFalse(player.removeCard(item1)); 105 | } 106 | 107 | @Test 108 | public void testSetNumVisibleCards() { 109 | int id = 1; 110 | PlayerModel player = new PlayerModel(id); 111 | Item item1 = new Item("BOOK", id); 112 | Item item2 = new Item("POTION", id); 113 | Item item3 = new Item("ARROW", id); 114 | Item item4 = new Item("FISH", id); 115 | int numVisibleCards1 = 1; 116 | int numVisibleCards2 = 3; 117 | int numVisibleCards3 = 4; 118 | List itemList = Arrays.asList(item1, item2, item3, item4); 119 | player.setCards(itemList); 120 | player.setNumVisibleCards(numVisibleCards1); 121 | player.flipCards(); 122 | assertEquals(1, player.getNumQuestCards()); 123 | assertEquals(3, player.getNumDeckCards()); 124 | player.setNumVisibleCards(numVisibleCards2); 125 | player.flipCards(); 126 | assertEquals(3, player.getNumQuestCards()); 127 | assertEquals(1, player.getNumDeckCards()); 128 | player.setNumVisibleCards(numVisibleCards3); 129 | player.flipCards(); 130 | assertEquals(3, player.getNumQuestCards()); 131 | assertEquals(1, player.getNumDeckCards()); 132 | } 133 | 134 | @Test 135 | public void testFlipCards() { 136 | int id = 1; 137 | PlayerModel player = new PlayerModel(id); 138 | Item item1 = new Item("BOOK", id); 139 | Item item2 = new Item("POTION", id); 140 | Item item3 = new Item("ARROW", id); 141 | Item item4 = new Item("FISH", id); 142 | int numVisibleCards = 3; 143 | List itemList = Arrays.asList(item1, item2, item3, item4); 144 | player.flipCards(); // shall not produce errors 145 | assertEquals(0, player.getNumQuestCards()); 146 | assertEquals(0, player.getNumDeckCards()); 147 | player.setCards(itemList); 148 | player.flipCards(); 149 | assertEquals(0, player.getNumQuestCards()); 150 | assertEquals(4, player.getNumDeckCards()); 151 | player.setNumVisibleCards(numVisibleCards); 152 | player.flipCards(); 153 | assertEquals(3, player.getNumQuestCards()); 154 | assertEquals(1, player.getNumDeckCards()); 155 | player.flipCards(); 156 | assertEquals(3, player.getNumQuestCards()); 157 | assertEquals(1, player.getNumDeckCards()); 158 | } 159 | 160 | @Test 161 | public void testPlayerToString() { 162 | int id = 1; 163 | PlayerModel player = new PlayerModel(id); 164 | Vector2 pos = new Vector2(1, 2); 165 | TileModel tile = new TileModel("1111", pos); 166 | player.setTile(tile); 167 | Item item1 = new Item("BOOK", 1); 168 | Item item2 = new Item("POTION", 1); 169 | List itemList = new ArrayList<>(); 170 | itemList.addAll(Arrays.asList(item1, item2)); 171 | player.setCards(itemList); 172 | assertEquals("2 6 6 1111", player.playerToString()); 173 | } 174 | 175 | @Test 176 | public void testMoveToPos() { 177 | int id = 1; 178 | PlayerModel player = new PlayerModel(id); 179 | assertEquals(new Vector2(6, 6), player.getPos()); 180 | Vector2 pos = new Vector2(6, 5); 181 | player.move(pos); 182 | assertEquals(pos, player.getPos()); 183 | } 184 | 185 | @Test 186 | public void testMoveInDirection() { 187 | int id = 1; 188 | PlayerModel player = new PlayerModel(id); 189 | player.move(Constants.Direction.UP); 190 | assertEquals(new Vector2(6, 5), player.getPos()); 191 | player.move(Constants.Direction.LEFT); 192 | assertEquals(new Vector2(5, 5), player.getPos()); 193 | player.move(Constants.Direction.DOWN); 194 | assertEquals(new Vector2(5, 6), player.getPos()); 195 | player.move(Constants.Direction.RIGHT); 196 | assertEquals(new Vector2(6, 6), player.getPos()); 197 | } 198 | } -------------------------------------------------------------------------------- /src/test/java/com/codingame/game/Model/TileModelTest.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Model; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertNull; 6 | import static org.junit.Assert.assertTrue; 7 | 8 | import org.junit.Test; 9 | 10 | import com.codingame.game.Utils.Constants; 11 | import com.codingame.game.Utils.Vector2; 12 | 13 | public class TileModelTest { 14 | 15 | @Test(expected=AssertionError.class) 16 | public void testAssertionsEnabled() { 17 | assert false; 18 | } 19 | 20 | @Test(expected=AssertionError.class) 21 | public void testTileIncorrectPatternDigit() throws AssertionError { 22 | TileModel tile = new TileModel("1112", new Vector2(1, 2)); 23 | } 24 | 25 | @Test(expected=AssertionError.class) 26 | public void testTileIncorrectPatternLength() throws AssertionError { 27 | TileModel tile = new TileModel("11100", new Vector2(1, 2)); 28 | } 29 | 30 | @Test(expected=AssertionError.class) 31 | public void testTileSetIncorrectItem() throws AssertionError { 32 | TileModel tile = new TileModel("1111", new Vector2(1, 2)); 33 | tile.setItem(new Item("APPLE", 0)); 34 | } 35 | 36 | @Test(expected=AssertionError.class) 37 | public void testTileSetItem() throws AssertionError { 38 | TileModel tile = new TileModel("1111", new Vector2(1, 2)); 39 | tile.setItem(new Item("BOOK", 0)); 40 | tile.setItem(new Item("ARROW", 0)); 41 | } 42 | 43 | @Test(expected=AssertionError.class) 44 | public void testTileRemoveItem() throws AssertionError { 45 | TileModel tile = new TileModel("1111", new Vector2(1, 2)); 46 | tile.removeItem(); 47 | } 48 | 49 | @Test(expected=AssertionError.class) 50 | public void testOpponentTileNoItem() throws AssertionError { 51 | TileModel tile = new TileModel("1111", new Vector2(1, 2)); 52 | tile.opponentTileToString(); 53 | } 54 | 55 | @Test 56 | public void testSetPosExposure() { 57 | Vector2 pos = new Vector2(1, 2); 58 | TileModel tile = new TileModel("1111", pos); 59 | assertTrue(tile.getPos().equals(pos)); 60 | pos.setX(4); 61 | pos.setY(3); 62 | assertFalse(tile.getPos().equals(pos)); 63 | } 64 | 65 | @Test 66 | public void testGetPosExposure() { 67 | Vector2 pos = new Vector2(1, 2); 68 | TileModel tile = new TileModel("1111", pos); 69 | assertTrue(tile.getPos().equals(pos)); 70 | Vector2 newPos = tile.getPos(); 71 | newPos.setX(4); 72 | newPos.setY(3); 73 | assertTrue(tile.getPos().equals(pos)); 74 | } 75 | 76 | @Test 77 | public void testConstructorPosExposure() { 78 | Vector2 pos = new Vector2(1, 2); 79 | TileModel tile = new TileModel("1111", pos); 80 | assertTrue(tile.getPos().equals(pos)); 81 | pos.setX(4); 82 | pos.setY(3); 83 | assertTrue(tile.getPos().equals(new Vector2(1, 2))); 84 | } 85 | 86 | @Test 87 | public void testDirectionCheck() { 88 | Vector2 pos = new Vector2(1, 2); 89 | TileModel tile = new TileModel("1111", pos); 90 | assertTrue(tile.hasDirection(Constants.Direction.UP)); 91 | assertTrue(tile.hasDirection(Constants.Direction.RIGHT)); 92 | assertTrue(tile.hasDirection(Constants.Direction.DOWN)); 93 | assertTrue(tile.hasDirection(Constants.Direction.LEFT)); 94 | tile = new TileModel("0100", pos); 95 | assertFalse(tile.hasDirection(Constants.Direction.UP)); 96 | assertTrue(tile.hasDirection(Constants.Direction.RIGHT)); 97 | assertFalse(tile.hasDirection(Constants.Direction.DOWN)); 98 | assertFalse(tile.hasDirection(Constants.Direction.LEFT)); 99 | tile = new TileModel("0010", pos); 100 | assertFalse(tile.hasDirection(Constants.Direction.UP)); 101 | assertFalse(tile.hasDirection(Constants.Direction.RIGHT)); 102 | assertTrue(tile.hasDirection(Constants.Direction.DOWN)); 103 | assertFalse(tile.hasDirection(Constants.Direction.LEFT)); 104 | tile = new TileModel("0001", pos); 105 | assertFalse(tile.hasDirection(Constants.Direction.UP)); 106 | assertFalse(tile.hasDirection(Constants.Direction.RIGHT)); 107 | assertFalse(tile.hasDirection(Constants.Direction.DOWN)); 108 | assertTrue(tile.hasDirection(Constants.Direction.LEFT)); 109 | } 110 | 111 | @Test 112 | public void testRotatePattern() { 113 | Vector2 pos = new Vector2(1, 2); 114 | TileModel tile = new TileModel("1000", pos); 115 | assertTrue(tile.hasDirection(Constants.Direction.UP)); 116 | tile.rotate(0); 117 | assertTrue(tile.hasDirection(Constants.Direction.UP)); 118 | tile.rotate(1); 119 | assertTrue(tile.hasDirection(Constants.Direction.RIGHT)); 120 | tile.rotate(2); 121 | assertTrue(tile.hasDirection(Constants.Direction.LEFT)); 122 | tile.rotate(3); 123 | assertTrue(tile.hasDirection(Constants.Direction.DOWN)); 124 | } 125 | 126 | @Test 127 | public void testGetSetItem() { 128 | Vector2 pos = new Vector2(1, 2); 129 | TileModel tile = new TileModel("1000", pos); 130 | assertFalse(tile.hasItem()); 131 | assertNull(tile.getItem()); 132 | Item item = new Item("BOOK", 0); 133 | tile.setItem(item); 134 | assertTrue(tile.hasItem()); 135 | assertTrue(tile.getItem().equals(item)); 136 | item = new Item("ARROW", 1); 137 | assertFalse(tile.getItem().equals(item)); 138 | Item item2 = tile.getItem(); 139 | item2 = new Item("ARROW", 1); 140 | assertFalse(tile.getItem().equals(item2)); 141 | } 142 | 143 | @Test 144 | public void testRemoveItem() { 145 | Vector2 pos = new Vector2(1, 2); 146 | TileModel tile = new TileModel("1111", pos); 147 | Item item = new Item("BOOK", 0); 148 | tile.setItem(item); 149 | tile.removeItem(); 150 | assertNull(tile.getItem()); 151 | } 152 | 153 | @Test 154 | public void testPatternToString() { 155 | Vector2 pos = new Vector2(1, 2); 156 | TileModel tile = new TileModel("1010", pos); 157 | assertEquals("1010", tile.patternToString()); 158 | } 159 | 160 | @Test 161 | public void testTileToString() { 162 | Vector2 pos = new Vector2(1, 2); 163 | TileModel tile = new TileModel("1010", pos); 164 | Item item = new Item("BOOK", 0); 165 | tile.setItem(item); 166 | assertEquals("BOOK 1 2 0", tile.tileToString()); 167 | tile.removeItem(); 168 | assertEquals("", tile.tileToString()); 169 | } 170 | 171 | @Test 172 | public void testMoveTileToPos() { 173 | Vector2 pos = new Vector2(1, 2); 174 | TileModel tile = new TileModel("1010", pos); 175 | Vector2 newPos = new Vector2(3, 4); 176 | tile.move(newPos); 177 | assertEquals(newPos, tile.getPos()); 178 | newPos = new Vector2(0, 0); 179 | tile.move(newPos); 180 | assertEquals(newPos, tile.getPos()); 181 | newPos = Vector2.MINUS_ONE; 182 | tile.move(newPos); 183 | assertEquals(newPos, tile.getPos()); 184 | } 185 | 186 | @Test 187 | public void testMoveTileInDirection() { 188 | Vector2 pos = new Vector2(1, 2); 189 | TileModel tile = new TileModel("1010", pos); 190 | tile.move(Constants.Direction.UP); 191 | assertEquals(new Vector2(1, 1), tile.getPos()); 192 | tile.move(Constants.Direction.RIGHT); 193 | assertEquals(new Vector2(2, 1), tile.getPos()); 194 | tile.move(Constants.Direction.DOWN); 195 | assertEquals(new Vector2(2, 2), tile.getPos()); 196 | tile.move(Constants.Direction.LEFT); 197 | assertEquals(new Vector2(1, 2), tile.getPos()); 198 | } 199 | 200 | @Test 201 | public void testIsThreeWayPlus() { 202 | TileModel tile1 = new TileModel("0000",new Vector2(1, 2)); 203 | TileModel tile2 = new TileModel("1010",new Vector2(1, 2)); 204 | TileModel tile3 = new TileModel("1110",new Vector2(1, 2)); 205 | TileModel tile4 = new TileModel("1111",new Vector2(1, 2)); 206 | assertFalse(tile1.isThreeWayPlus()); 207 | assertFalse(tile2.isThreeWayPlus()); 208 | assertTrue(tile3.isThreeWayPlus()); 209 | assertTrue(tile4.isThreeWayPlus()); 210 | } 211 | 212 | @Test 213 | public void testOpponentTile() { 214 | Vector2 pos1 = new Vector2(1, 2); 215 | TileModel tile = new TileModel("1010", pos1); 216 | Item item = new Item("BOOK", 0); 217 | tile.setItem(item); 218 | assertEquals("BOOK 1 2 1", tile.opponentTileToString()); 219 | tile.setPos(Constants.TILE_MODEL_POSITIONS.get(0)); 220 | tile.setPlayerId(0); 221 | assertEquals("BOOK -2 -2 1", tile.opponentTileToString()); 222 | tile.setPos(Constants.TILE_MODEL_POSITIONS.get(1)); 223 | tile.setPlayerId(1); 224 | assertEquals("BOOK -1 -1 1", tile.opponentTileToString()); 225 | } 226 | 227 | @Test 228 | public void testPlayerId() { 229 | Vector2 pos = new Vector2(1, 2); 230 | Integer id = 1; 231 | TileModel tile = new TileModel("1010", pos); 232 | assertNull(tile.getPlayerId()); 233 | tile.setPlayerId(id); 234 | assertEquals(id, tile.getPlayerId()); 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /src/test/java/com/codingame/game/PlayerTest.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import com.codingame.game.Model.PlayerModel; 4 | import com.codingame.game.Utils.Constants; 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.rules.ExpectedException; 8 | 9 | import java.util.List; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | public class PlayerTest { 14 | 15 | @Test(expected=AssertionError.class) 16 | public void testAssertionsEnabled() { 17 | assert false; 18 | } 19 | 20 | @Test 21 | public void testCreateGetModel() { 22 | Player player = new Player(); 23 | int id = player.getIndex(); 24 | PlayerModel model = player.createPlayer(); 25 | assertEquals(id, model.id); 26 | assertEquals(model, player.getPlayer()); 27 | } 28 | 29 | @Test 30 | public void testGetModel() { 31 | Player player = new Player(); 32 | int id = player.getIndex(); 33 | assertEquals(id, player.createPlayer().id); 34 | } 35 | } -------------------------------------------------------------------------------- /src/test/java/com/codingame/game/Utils/Vector2Test.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.Utils; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.*; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | 10 | /** 11 | * Test suite for Vector2 specs. 12 | */ 13 | public class Vector2Test { 14 | 15 | @Test(expected=AssertionError.class) 16 | public void testAssertionsEnabled() { 17 | assert false; 18 | } 19 | 20 | @Test 21 | public void testNegativeConstants() { 22 | Vector2 vector1 = Vector2.MINUS_ONE; 23 | assertEquals(-1, vector1.getX()); 24 | assertEquals(-1, vector1.getY()); 25 | Vector2 vector2 = Vector2.MINUS_TWO; 26 | assertEquals(-2, vector2.getX()); 27 | assertEquals(-2, vector2.getY()); 28 | } 29 | 30 | @Test 31 | public void testVectorConstructorMutator() { 32 | Vector2 vector = new Vector2(3, 5); 33 | assertEquals(3, vector.getX()); 34 | assertEquals(5, vector.getY()); 35 | vector.setX(4); 36 | vector.setY(2); 37 | assertEquals(4, vector.getX()); 38 | assertEquals(2, vector.getY()); 39 | } 40 | 41 | @Test 42 | public void testVectorFromAnotherVector() { 43 | Vector2 vector1 = new Vector2(3, 5); 44 | Vector2 vector2 = new Vector2(vector1); 45 | vector1.setX(4); 46 | vector1.setY(2); 47 | assertEquals(3, vector2.getX()); 48 | assertEquals(5, vector2.getY()); 49 | } 50 | 51 | @Test 52 | public void testVectorWrapping() { 53 | Vector2 vector1 = new Vector2(3, 5); 54 | Vector2 vector2 = new Vector2(2, -3); 55 | Vector2 vector3 = new Vector2(-4, 2); 56 | Vector2 vector4 = new Vector2(1, 4); 57 | vector1.wrap(vector2); 58 | assertEquals(5, vector1.getX()); 59 | assertEquals(2, vector1.getY()); 60 | vector1.wrap(vector3); 61 | assertEquals(1, vector1.getX()); 62 | assertEquals(4, vector1.getY()); 63 | vector1.wrap(vector4); 64 | assertEquals(2, vector1.getX()); 65 | assertEquals(1, vector1.getY()); 66 | vector1.wrap(vector3); 67 | assertEquals(5, vector1.getX()); 68 | assertEquals(3, vector1.getY()); 69 | } 70 | 71 | @Test 72 | public void testVectorAddition() { 73 | Vector2 vector1 = new Vector2(3, 5); 74 | Vector2 vector2 = new Vector2(-4, 2); 75 | vector1.add(vector2); 76 | assertEquals(-1, vector1.getX()); 77 | assertEquals(7, vector1.getY()); 78 | 79 | } 80 | 81 | @Test 82 | public void testVectorMovement() { 83 | Vector2 vector1 = new Vector2(2, 4); 84 | vector1.add(Vector2.UP); 85 | assertEquals(2, vector1.getX()); 86 | assertEquals(3, vector1.getY()); 87 | vector1.add(Vector2.RIGHT); 88 | assertEquals(3, vector1.getX()); 89 | assertEquals(3, vector1.getY()); 90 | vector1.add(Vector2.DOWN); 91 | assertEquals(3, vector1.getX()); 92 | assertEquals(4, vector1.getY()); 93 | vector1.add(Vector2.LEFT); 94 | assertEquals(2, vector1.getX()); 95 | assertEquals(4, vector1.getY()); 96 | } 97 | 98 | @Test 99 | public void testVectorEquality() { 100 | Set set = new HashSet<>(); 101 | Vector2 vector1 = new Vector2(3, 5); 102 | Vector2 vector2 = new Vector2(2, 4); 103 | set.add(vector1); 104 | set.add(vector2); 105 | assertTrue(set.contains(vector1)); 106 | assertTrue(set.contains(vector2)); 107 | assertEquals(2, set.size()); 108 | vector1.setX(2); 109 | vector1.setY(4); 110 | assertTrue(set.contains(vector1)); 111 | assertTrue(set.contains(vector2)); 112 | assertEquals(2, set.size()); 113 | } 114 | 115 | @Test 116 | public void testVectorToString() { 117 | Vector2 vector = new Vector2(3, 5); 118 | assertEquals("3 5", vector.toString()); 119 | } 120 | 121 | @Test 122 | public void testVectorToTooltip() { 123 | Vector2 vector = new Vector2(3, 5); 124 | assertEquals("pos: (3, 5)", vector.toTooltip()); 125 | } 126 | } -------------------------------------------------------------------------------- /src/test/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | # Configuration 2 | name = PropertiesConfig 3 | status = WARN 4 | 5 | appender.console.type = Console 6 | appender.console.name = CONSOLE 7 | appender.console.layout.type = PatternLayout 8 | appender.console.layout.pattern = [%d{yyyy/MM/dd HH:mm:ss}][GF] %-5p : %c{1} - %m%n 9 | 10 | rootLogger.level = warn 11 | rootLogger.appenderRef.console.ref = CONSOLE 12 | --------------------------------------------------------------------------------