Spring Challenge 2020 - Feedback & Strategy

Thanks for the PM! Do you run this on each pac in sequence (decide pac 1 > decide pac 2…)? Would that favor the first pac since it would have less often a thruPac (being the only one to move)?

First, C++ is generally faster than most of the other languages here.

Second, the winners tend to be professionals or college students, so they’re either really good at C++ or they’re learning it in school.

At least, I think…

1 Like

I want to say that companies competition is a great thing. That was the main motivator to improve for me even when thought that I couldn’t find what to improve.

Finished 236th (golden league)

My solution
Unfortunately this time my solution became a bunch of "if"s very quickly.

For each pac:

  1. Decide whether it wants to use the ability:
    1.1) SWITCH if we have an enemy in the next cell who can beat us
    1.2) SWITCH if we want to resolve a collision by switching type (only if our score is less than the opponent’s)
    1.3) SPEED if we are not blocked
  2. Add everything to the obstacles grid (including other pacs and stronger enemies)
  3. If we have a stronger pack around also add cells that it can attack as obstacles
  4. Calculate distances to each cell using Lee algorithm
  5. Find the closest reachable super-pellet and chose it as target path
  6. If target path is not selected choose the most desirable pellet to go after (weight = pellet neighbors count - distance - how many steps ago we seen it last time divided by 10)
  7. If target path is not selected find out if we have our pack standing nearby to make a step towards him (to his current position)
  8. If target path is not selected just go to the center
  9. If we had a collision last frame, check that we didn’t bump into our pack (a pack in 2 steps who also had a collision)
    9.1) If we did and we’re not the first by the order, don’t move this turn
  10. Calculate the next step we going do to reach the target and mark it as an obstacle to other pacs this turn
  11. If we are sped up, calculate the second step we’ll do this turn
    11.1) If our target is further than one step, use the step from path calculation algorithm
    11.2) If our target is in one step
    11.2.1) Extrapolate the step and check if it’s not blocked by a wall or our pack in 2 turns
    11.2.2) Run weight calculation again from the point that we’ll stand in one step and choose the best move
    11.3) Save the second step to the obstacles for other pacs in this turn
  12. MOVE to the appropriate position calculated above

Thanks a lot for an awesome event, and especially for updating C# environment! (I would have skipped this contest if .NET Core hadn’t been deployed.) The game was balanced, clear rules, not too many mechanics. (Maybe SPEED implementation/visualization was a little bit confusing.)

What was really frustrating is the lack of some testing environment or local runner. I didn’t manage to use CGBenchmark; I would try to implement my own tool if the API was well documented.

The outcome of any tiny change is unpredictable; you are never sure if this is regression or improvement. With so many ideas of enhancement I was paralyzed with the fear of breaking smth accidentally, and not being able to detect regression, or false-positively reject some good ideas.

Uncertainty :(

How do you organize you dev environment to test for regression and benchmark the improvements, to make the progress more evidence based?

(I hope, the next event will be more “continuous”, like Code Royal or Coders Strike Back :))

2 Likes

@Lysk Good question - The algorithm is stateless; i.e. it doesn’t favor any pac based on the order you run it. It just propagates the scores back up to the root and tells you which direction you want to go but doesn’t commit the pacman to moving any way.

The main idea is that because we are doing this DFS on the graph that would be produced by djikstra’s, it means that if you open up a node that contains a pacman, this pacman will have closer access to all the points under that subtree than the pacman at the root of the tree. So when we return the score and propagate it back to the parent, I choose to return the secondMax value instead of max.

2 Likes

“Can you give a single example where simulation isn’t better?”

Thats exactly the problem. Those games had became mostly a feast of search algorithims, instead of favoring heuristics, logic and implementation of some kind of AI. It´s all about brute force (and its variants that just filter some results, but are a form of dumb possible results testing). We will never create a true AI just testing every possible solution.

2 Likes

I ended up at the bottom of gold league. Was in the middle of it and then I changed something that dropped me significantly and no matter what I tried to fix it, it didn’t fix my leaderboard position.

As for the strategy, mine was pretty simple. BFS, track taken cells by my other pacs to avoid collisions, track distance 1 and 2 cells (surroundings) and identify any threats, and then finally cell scoring based on what is in the cell.

Here’s a pseudocode for my rules and cell scoring: https://gist.github.com/depy/d6969158cedc81c6c323d099ed445bf8

All in all, given it was my first CG competition - I use CG but never competed - and given I used Ruby which is not blazingly fast, then I’m pretty happy with the outcome.

If I had more time to refactor my code or if I wrote my code better from the beginning, the next thing I would add would be checking if next enemies that are in sight are a threat and can also move to the same cell I plan to move to and avoid that - I got eaten so many time because I planned to turn a corner and the enemy 2 cells away in front of me had the same plans for the next move.

And the other thing I did not implement is taking into account that “rules” change a bit if I have “speed” activated or the enemy has it activated.

One question I have for everyone is how did you avoid getting “corner killed”?

2 Likes

Finished #4
Many thanks to CodinGame and all the competitors for this very entertaining contest :slight_smile:

For the first two thirds of the competition time-span, I tried to implement a full tracker of the opponents’ paths, similar to what I did for Ocean of Code.
I really enjoyed this aspect in UTG and OOC contests, trying to extract the maximum of hidden information from the few inputs we have.
It was very challenging due to the exponentially increasing number of states, and I was close to give up, facing all these timeouts :wink: . I’m glad I persevered, this tracker was certainly the only strength of my bot :slight_smile:
It allowed me to remove a large amount of the pellets gathered by the opponent, and to avoid some (sadly not all :slight_smile: ) of the bad encounters at the corners.

The rest of my bot is the selection of an action successively for each pac, only based on heuristics on the current state of the game. I did not take the time to implement a MCTS or other search algorithm, simulating next turns or trying to maximize farming.

Tracker
Each bot had a list of possible states (position, cooldowns, type, and visited cells).
The tracker maintained a list of possible valid combinations for those states.

Expected information :

  • Sets of possible pacs positions, types and cooldowns,
  • Removal of most of the pellets gathered by the opponent.

Information we had :

  • Disappeared pellets in pacs’ visible area (+ big pellets),
  • Increment of our and opponent scores,
  • State of our pacs,
  • State (or absence) of visible opponent’s pacs,
  • Possible actions for a pac, based on its state, or based on combination of states (valid moves imply no collision).

How to use it :

  • A disappeared big pellet means at least one pac was on this location last turn,
  • A disappeared pellet means at least one pac visited the cell on one of the previous turns,
  • A remaining pellet (or big pellet) means no pac ever visited the cell,
  • An increment of X in opponent’s score means that each valid new combination of pacs’ states, expanding a previous valid combination, must have eaten the exact amount of pellets leading to that score,
  • An unexpected loss of score means an opponent pac has taken the pellet or big pellet right under our nose,
  • Spotted pacs (and starting positions) : self explanatory :slight_smile:
  • Non-spotted pacs in visible area means no pac is here now,
  • Unexpected blocking (or death) of one of our pacs means an opponent pac is near us trying to move to the same cell,
  • Absence of blocking or still alive pacs means the opposite.

Details (feel free to skip :sweat_smile:) :

Pacs individual states :
Each pac state’s visited cells were reduced to their intersection with the possible remaining pellets at the beginning of the turn. The implied merging of states allowed to reduce now identical previously valid combinations of pacs formations.
Pacs were sorted by the amount of associated or expected states (dead first, then spotted, then pacs with the least previous states). Spreading of next evaluated pacs’ states could indeed benefit more pruning from the common properties of all new valid states for an already ‘expanded’ pac.
Taking into account the maximum and minimum amount of pellets gathered by other pacs and the score variation, we can deduce lower and upper bounds to the number of pellets gathered by the evaluated pac. In addition to gathered pellets count, verification also included (un)blocking, deaths (or absence of), invalid visited cells, etc.
The expansion of a state could identify previous states without children. Such an invalid state allowed the pruning of all previous combinations containing this state. Other pacs’ states referenced by invalidated combinations could themselves be invalidated if they did not appear in other valid combinations.
The intersection of visited cells for all remaining valid states associated with a pac allowed a first removal of remaining pellets.

Combinations :
After all these expansions of individual states, a set of new valid combinations was created from each previous valid combination, taking into account each new states expanded from those composing the combination. At each level of the cascaded loops, the evaluation of the new combinations was interrupted if requirements (gathering, killing, blocking, presence in one of the previous turn, etc.) could not be satisfied, whatever the state we chose for the next pacs (taking into account the common properties of the next pacs’ states).
Combinations leading to impossible combined moves due to collision between opponent pacs were filtered. When a seemingly valid combination was found, a last check involved merging of root combinations pacs’ visited cell and new pacs’ visited cells. If the variation of gathered cells between these 2 did not match the variation in opponent score, the new combination was discarded.
The intersection of visited cells for all valid combinations (union of visited cells for each pac) allowed a second removal of remaining pellets (and more merging/pruning of states and combinations next turn :slight_smile:).
All states belonging to none of the new combinations were then erased, and a new intersection pass lead to yet another removal of pellets…

Fallback :
It sometimes managed to track the opponent the whole game, but often the combinatorics was too high. In those cases, all combinations were cleared, but states expansion and pre-filters remained active. This resulted in a loss of performance, but was still usable. Most of the important pellet information was deduced before.
All in all, that was quite a complicated tracker, not sure it was worth it, but it was fun to implement (not so fun to debug :wink: ).

Game logic
The selection of each of our pac’s next move was done through the evaluation of the following steps, by order of importance (when an action was assigned to a pac, next step would not replace it) :

  • Attack : try to trap and kill pacs in dead end (some ifs involving cooldowns, length of dead end, pacs types, …),
  • Defense : flee, wait or switch to killer type if trapped in dead end (again, some ifs :slight_smile: ),
  • Enable speed : if cooldown ready, unless near big pellet or in danger (a known killer opponent is nearby, need to keep cooldown ready for switch or to flee),
  • Gather big-pellet (some ifs, could have largely been improved…),
  • Gather pellet (sort pellets by score for each pac ; select pac with the higher score then loop ; remove pellets on evaluated pac’s way for the evaluation of other pacs ; avoid dead-ends ; try to spread pacs),
  • Flee or fight if needed, instead of standing still.

At each step, every pac was evaluated independently. But before the validation of an action, I checked that the pac would not collide with other already assigned pacs, and would not be exposed to a possible killer pac.
A set of possible opponent pacs’ states (up to 8 per pac, if more pac was ignored) was deduced from tracker, to help with the evaluation of those steps.

This game logic is relatively simple, with no deep search of the best possible moves. And I’d like to think that the efficiency of the bot was due the accurate enough information extracted from the tracker.

Thanks for reading :wink:
And again, many thanks for this very entertaining contest, and congratulations to the winners :slight_smile:

21 Likes

Thanks to the creators, CG and congrats to the winners ! It was a good contest. For some reason I tend to not do very good on fog of war contests (WW, OOC) and this was no exception. I ended up #203, dissapointing since i had a lot of the complex mechanics that many of the top players implemented. I think with some tweaking and bug fixing I should be able to be higher in the ranking.

The different thing from the others that I did was a smitsimax as the search algo. That search gave me good results in other contests, but I struggled with it this time around, specially with the ucb parameters. I never managed to make it work properly, and in the end I just used it as the battle algorithm when one or more of my pacs are close to a rival. As the main search algo I used a depth 15 dfs for each pac, with reruns to prevent blocking each other.

Do you know when will the multi be available? Or at least some beta version of it…

Thanks again and looking forward for the next contest (end of year, ouch…)

5 Likes

Hello! I finished 76th in Legend.

My bot used a Search that was based on Smitsimax. This was my first contest on CG that I worked on from start to finish, so I was very excited to reach Legend.

First Turn

  1. Fill in the location of all enemy pacmen and all pellets.

  2. Calculate which pacmen has the shortest path to every super pellet.

  • If two pacmen are the same distance, but one pacman will kill the other, the weaker pacman should cede the super pellet to the stronger one.

Search Function

  1. For each pacman (mine and opponents), I built a tree of the pacman’s possible actions, with a depth of 4.

    a. Like most others, I only considered paths flowing outwards from the pacmen. They couldn’t turn around mid-path, unless they hit a wall.

    b. I did not make any assumptions about when a pacman has to use an ability, they could save it up if they wanted to.

  2. I ran Smitsimax. An action was chosen for each pacman, and those actions were simulated. This repeated until we reached a depth of four. At this point the board was scored. Repeat for N iterations.

Problems with my Search

At this point. I found that although Smitsimax was giving decent moves, it was hardly ever finding the optimal moves. So I added another layer to my algorithm. I decided to call it (for some reason) Lock and Rock. I think a lot of people did something similar, minus the Smitsimax.

Let’s say there are 8 pacmen. At this point we have the “best” set of actions for each pacman, as returned by Smitsimax.

Pacman1: NORTH, NORTH, EAST, EAST
Pacman2: SPEED, (NORTH, NORTH), (NORTH WEST), (WEST WEST)

Pacman8: SWITCH_SCISSORS, SOUTH, WEST, SOUTH

Let’s assume that Pacman2 - Pacman8 are going to follow those paths. But, what about Pacman1? We can try every possible action in Pacman1’s tree and evaluate it against the other pacmens’ actions. So given the other pacmens’ “locked” paths, determine which path is truly optimal for Pacman1. Repeat this process for the rest of the pacman. At this point all the pacmen are going to have a very good move.

I run this algorithm twice, and then for each pacman I do a final analysis on the two best actions for each pacman and choose the best one.

Evaluation Function

  • If pacmen have goals (to get a superpellet) +SCORE for getting close to that goal, or achieving it
  • -LOT for dying, +LITTLE for killing a pacman
  • +SCORE for eating pellets, -SCORE for opponent eating pellets
  • -SCORE for being far away from pellets
  • +SCORE for number of pellets in range (using something like voronoi)

Opponent Tracking

  • Because my Search also predicted the best moves for the opponent’s pacman, I assumed they made those moves, and marked any pellets that they would have eaten as “unlikely_pellets”
  • If I saw an opponent later in an unexpected spot, I would update their location.
  • If I didn’t see an opponent somewhere that I DID expect them, I would stop tracking them.
  • However, if I made a wrong guess I didn’t have any code to go backwards and figure out where they really might be, or what pellets they may or may not have eaten. I would have liked to do that if i had more time.

Overall, this was an excellent experience. Congrats again to all the winners, and thanks to CodinGame for a great contest!

9 Likes

26th Legend / Java

I spent very good time playing this contest, it was not so easy due to the fog of war and the number of moves combinations!

Pretty proud to have contributed to the 1st place of Thales and 9th of INSA Toulouse :grinning:

Wood to Bronze

First, it is important not to spend too much time when we do not know all the rules so i try to reach quickly bronze with the minimum of code and strategy.

Here, i just move pacs to the closest super pellet or if not any the closest pellet. I use speed whenever i can. For the distance, i used manhattan at this moment.

Strategy to reach Legend

Precomputed data
I use the first turn to compute as much possible data to save time after:

  • position : instantiate a Position object for each coordinate. Just a trick to avoid garbage collector pressure when creating too much objects

  • distance : for each position, save the distance to each other position using a BFS

  • line of sight : for each position save the positions in sight

  • moves positions : for each position save the positions we can reach with speed 1 and speed 2

  • deadend : mark position in deadend and save the position list associated to find the whole deadend position associated so it is possible to get the start and end easily

  • paths : list of possibles paths for each position computed using a DFS depth 10. Turn around is done only when reaching a deadend

Init data
A pellet board is created marking all empty position with no super pellet and no pac.
I use for that a bit board with one long per line.
At turn one, i deduct from my pacs positions the opponent pacs positions and type.

Update data
Each turn, i update my pellet board with information i have using pac line sight.
I try to guess also pacs positions. For that i have a list of possible position/visited for each opponent pac i update each turn so when the opponent reappears i can update my pellet board removing the visited positions.

Engine
I have implemented the engine so i can play easily actions and see what happen managing also collisions. I was thinking i could do a depth evaluation at this moment but when i try all permutations moves it was really too slow and i timeout regularly with 5 pac and depth 1… (my evaluation function was in cause too)

Compute
Like i said before my first try was to play all permutation moves and evaluate the game. It lets me reach silver but some timeouts in 5 pacs maps prevent me to reach gold.
Then i try to play iteratively. I compute the best move of one pac, then i compute the best move of the second one using the best move of the previous one and so on. It works well and let me reach legend (with some other stuffs).

Evaluation
In that game, it is important to control the whole map and to have a good distribution so i was thinking it is pretty like Tron game with voronoi. My main evaluation is based on that.

  • evaluate positions : affect points based on the distance of each pellet to the closest pac positive for my pac and negative for opponent pacs. value = pelletValue / dist
  • score : 10 * (myscore - oppscore)
  • kill : +/-500 for each pac alive
  • best path evaluation : evaluate the best path using precomputed dfs and a decrease at each depth. score += pelletValue * coeff^depth. i do not care of other pacs position for the path but if a position is closer to another pac i can not eat it bring 0

Tips & Tricks

  • Start : start strategy is really important and i have to precompute some moves. I affect super pellets to the closest pac and a pac can have multiple targets if he can reach another before other pacs. i compute also the opponent start move so i can during some time avoid taking into account in my evaluation function super pellet i am sure i can’t reach before the opponent. So my pac will not be in danger trying to reach them and also will not go on paths where pellets are already eaten.
  • Ability : i use speed as much as possible but i verify if my position is safe before using it. I can switch if i am in deadend and my position is not safe.
  • Prevent Hunting : to avoid hunting an opponent which can flee, i will simulate the opponent move if there position is not safe so my pac will attack only if the opponent can not flee.
  • Deadend Hunting : if i know i can hunt a pac in a deadend i do it. For that i have to be able to eat him before he flees or he recovers his ability
  • Pellets potentially eaten : as i try to guess the opponent i know the potentially visited positions so i decrease these pellets value in my evaluation function

Conclusion
Really pleasant contest. I can’t wait to see the leader strategy!

13 Likes

Why do you need separate DFS pass for paths if you already have BFS pass for distances? Can’t you save all-pairs paths info during BFS phase?

DFS gives me all the paths of a given length while BFS gives me only one as i do not visit positions multiple times

2 Likes

The challenge was very … “challenging”. I was hoping to see the code of other people! Is there any way to do so?
May be someone would send me their code in message so that I could compare my code with theirs?

why should use move to a random point
the game will end before this case
am I right?

Hello all,

#800 at the end, I don’t remember exactly, was #60 or so in silver and didn’t reach gold sadly, Python3

This was my first contest, and it was quite interesting, both as a developer and as a person / competitor.

I learnt A LOT of things in the contest, and here is how it went.

Start on May 8th, motivated and all. I quickly get in Bronze, and get all the rules. I follow my very first ideas, lacking actual big problem programming knowledge (I will work on codingame puzzles more to learn !):

  • Naive distance (x and y difference)
  • Define target by best cost (value - distance)

And it worked surprisingly well until the first Sunday, where I fell quite hard.

One big frustration of the contest for me was the lack of information about timeouts : like, my bot timeouts and I don’t even have the full print, and also sometimes not the error that lead to that… It makes debugging quite hard.

I understood I would need better ideas. So I started looking for things I’ve learnt back in school, such as A*… Worked quite fine, but still not enough for silver.

Then, looking on the forum, more ideas, like BFS (note : I’m talking about calculating distances, not simulating the game), registering the state of cells as you pass through them and see them…

In the end, I was defining targets based on real distance, available new informations (i.e. “see-able” cells - “known no pellet” cell), pellet value of course, and dead ends. Then, defining the next move (2 cells or one cell depending on Speed) based on the cost of the move (-1 if it gets you closer to the target, -1 if you eat a pellet) and the obstacles (own pacs moves are considered as obstacles, and opponent position + neighbor cells for opponent are considered as obstacles if he is of a dominant type or can instantly change type). I actually never used SWITCH for the final version of the bot! All in all I’m quite satisfied with that, as I learned A LOT of things, and didn’t feel confident enough to try and implement any simulation.

Also, it was a good learning opportunity as a person. I got discouraged on Thursday 14th as I couldn’t get out of bronze and as all my ideas were reaching the timeout… And hopefully found motivation again to “rethink” my algorithms on the Saturday, reaching silver and almost gold in the process. For the personal lessons, I should remember the following for the next contest :

  • Keep as much information as possible at all moments : memory is not an issue, until it is (but it was not an issue in that contest)
  • Don’t discard ideas because you feel they’ll timeout and you won’t be able to debug them, try and you may have good surprises
  • If ideas time out, try to rethink the algorithm (doing DFS for all single cells is quite a bad idea, yeah, but sometimes you don’t see the bigger picture…)
  • Keep trying new things. You have absolutely nothing to lose if your implementation or idea fails, and that’s the best way to learn.
  • Check for bugs across leagues with new rules (Silver bugged my code in ways I didn’t imagine… as dead pacs were visible, they were at first considered as obstacles…)

All in all, the contest was pleasant, but sometimes really frustrating (not knowing what timed out, or when it timed out is pretty frustrating (in my case at least, trying to print into a while that would timeout would not print anything :(). I will definitely consider participating in the fall challenge and hope I can reach gold this time!

1 Like

Sorry if the answer is obvious, but how do you evaluate the “30 closest” pellets?

Hello everyone,

I tried to be invested in this challenge but I have done a lot of wrong decisions at the beginning and didn’t make it to legend before the end.

I am now 24th in legend in multi thanks to few improvements not finished before the end of the contest.

FIRST LAP

  • Data computed at first lap:
  • Distances from every point to every point (stored in a map)
  • Possible moves of distance 1 and 2 from any position
  • Deadend cells
  • Groups of cells (cells between two intersections or between an intersection and a deadend cell)
  • Associate BIG PELLETS to pac-mans (several versions, never perfect…)

MY PATH
I only compute all paths at a distance of 7, with the possibility to visit a cell twice.

I currently store the path taken by my pac to avoid taking same tiles with other pacs. But I do it only once so first arrived first served… It would be a great improvement to perform it in different orders and find the best situation.

Eating an opponent with simple math:
dist <= diffSpeed && dist<=pac.cooldown
If this is not true, I have another rule if my opponent is in a deadend cell:
distToLastDeadCells - speedTurnsLeft <= pac.cooldown
In other word, if I can reach the end of the deadend before he can cooldown, I can purchase him.

In order to add it in my evaluation function, I add a huge score to reach my opponent position and the positions at a distance of 1 around him.
My evaluation funciton:
SCORE = 1000000*eatingPosition*turn^0.9 + 1000 * pelletPercentage*0.9^turn – 10000*riskBeingEaten*0.9^turn + 10*minDistMyOtherPacs (+ 100*minDistNearestPellet if no pellet taken)

OPPONENT TRACKING

What didn’t worked:
I tried to store the possible paths of my invisible opponents, but it was always ending in timeout even after cutting many branches. I have seen in some feedbacks that some have been able to do it, CONGRATULATIONS!

I finally only stored the possible opponent positions and use it as a coefficient for remaining pellets. Once I saw an opponent, if the maxDist - shortestDistFromLastSeenPosition < 8. I was performing all the possible paths and was removing the surely taken and applying a coefficient on maybe taken one.
I am also doing it when a big pellet have been removed.

PELLETS

I stored a matrix of percentage per tile according to opponent’s moves.

I didn’t see if other players did it too, but I am using groups (the one computed during the first turn).
If all the visible cells of a group are taken, I assume that all the invisible ones are taken too. I keep them just in case they are the only remaining once.

COLLISIONS

Avoid collisions with my own pac (if collision, the one with the smallest id won’t move).

If collision with an opponent, keep trying to reach the position without using my cooldown. If he uses its cooldown, I will be in the desired spot. If he switched, I can now switch too. Otherwise I will speed.

POWER

If in danger (opponent could eat me directly), or in a deadend cells and a dangerous opponent see me, switch type. Otherwise SPEED.

Thanks to one of the feedback, I implemented the fact that if more than 60% of my intersections tiles at a distance <=10 are at an odd distance, I prefer to move first and then SPEED. This leads to have more seen tiles naturally. And knowledge is power :grinning:

IMPROVEMENTS

  • When two or more of my pellets are near, find the best paths by computing their best paths in different orders.
  • Compute opponent position when collision with an unseen opponent.
  • Being close to opponent I could eat? Being far to opponent to could eat me?
  • Voronoi for nearest pellet. I tried once but results were worst. Maybe I have to perform it only at the end game. Or there was a bug in my code…

Thank a lot for this contest, it was interesting even if my first ideas were bad. I still prefer longest contest because I am always having a lack of time.

4 Likes

In my case I calculated the manhattan distance for every pellet and then sorted the list.

I could have also walked the graph keeping track of the depth, but the first method was fast enough.

1 Like

Legend 5th.

Great contest, I had so much fun :slight_smile:

【Algorithm】
Beam Search from each Pac. The depth is 8 turns. (To be exact, I used Chokudai Search, but this is almost the same as beam search)
Doing this search twice. (For example, if we have 5 Pacs, search 10 times.)
First one is to determine the order of Pacs for the main search. This uses 10 milliseconds.
Sort in order from the Pac who can create the game state with the highest evaluation value.
In this search, all teammates and opponents other than himself as a stopped.

Second one is the main search, to determine the action to output. This uses 35 milliseconds.
The action will be decided each Pacs in the priority order decided in the first search.
In this search, all teammates that has lower priority order and all opponents as a stopped. (But opponents are sometimes move to avoid death, details are described in the “Customize Simulation” Section.)

Using the same evaluation function in both searches.

【Evaluation Function】

1.Number of turns taken to eat super pellets
This is the most important evaluation. I have wanted to make the Pacs to eat Super Pellets as fast as they can.

2.Number of pellets eaten
The cells that are out of sight are substituted with the pre-calculated probability of pellets.

3.Number of pellets around each Pacs
Doing bfs and count the number of pellets on each path. Select the best path for each Pac, then evaluate the number of pellets on that path.

4.number of dead or checkmated oppontent Pacs
If the pac is in a dead end and he can’t get out of it or use abilities earlier than my Pac can reach, the opponent Pac is checkmated.
Dead = 10pellet, Checkmated = 9pellet score bonus.

【Predict Opponent Positions and Pellet Probabilities】

Doing bfs from the last spot that an opponent is found and guess his current position using the number of passed turns.
I don’t to consider about turning back on the way. So if he goes to the edge of the map, he is treated as missing.
We can narrow down the opponents’s position to some extent from the information that “the cell with a pellet is not passed by any Pacs” (In order to do this, also keep the opponent’s movement path).
Additionary, by calculating all the positions and paths where the opponents may exist, we can also calculate the probability of existence of pellets in each cell that are out of sight.

Then, when an opponent comes in sight, we can (roughly) calculate the path and determine the pellets eaten by the opponent.

【Customize Simulation】

When the simulation in the Beam Search described in the “Algorithm” section, in order to determine a more realistic Pac kill, I customized the simulation as follows.
1.My Pacs will be treated as dead when it enters the movable range of an opponent Pac who is stronger type to my Pac (I didn’t give a negative evaluation to this, because he is already disadvantaged in that he cannot move afterwards and get more pellets.)
2.When at the same cell as an opponent weaker to my Pac, let the opponent escape to neighbor cell, or use the ability if his AbilityCooldown equals 0. This allows to perform a simulation that kills only opponents who have no escape route (=checkmated).

【Conclusion】

My bot has a strong checkmate routine, eating a lot of opponent Pacs in many games.
Prioritizing attacks over exploration may reflect my personality haha

20 Likes