Hey, if you’re running your bruteforce on your possible commands (collect ore, place radar or kamikaze), how do you know that 2 robots will be next to one another next turn if the target is more than 1 turn away?
I finished #16. I thought the contest was a lot of fun, and that the key part was actually how many different ways one could think of to extract as much information as possible from all situations. The strategies then just naturally came along to react to that information.
I did pretty much the same thing as eulerscheZahl and funny enough, I finished right behind him. I gave scores to all cells depending on what my robots were supposed to do, kept only the top scores and then ran a bruteforce to assign each robot to a given task to have an optimal mix given my constraints. I also had features already discussed like potential enemy trap detection (the exact same one vrampal described) and removal (same as DaFish), stash construction (starting with the ones with the most ores), opportunistic dig of enemy stash etc… 2 other small features not yet discussed were finding what the next objective will be right before basing to save 1-3 movement cells on your next trip, and trying to blow up returning enemy robots when my own robot didn’t have time to collect one more ore before the end of the game.
I do believe that you had to drop at least a couple random bombs to protect yourself against people trying to steal your stashes, which became meta towards the end. I however did not have time to implement any sort of protection against the enemy kamikazing on their or my bombs, so that did backfire a few times (not to mention I got destroyed by the few bots trying to actively blow me up).
Thanks to the whole CG team for a great contest!
I compute the arrival time. If it only differs by 1 (=digging time), I see them in danger. You will still see it happen, but I prioritize other plans.
I came a distant third after karliso and teccles. Thanks to CodinGame, Amadeus, and all the players for a fun competition
For a while I just mined straightforwardly and sometimes placed traps where I happened to mine.
Then for a while I ran a dedicated hunter-killer bot who stood with a trap at x=1. He tried to predict where returning enemy robots would be and intercept them with traps to get 2-for-1 trades. This worked at low levels but wasn’t useful against top bots, so eventually I turned it off.
At some point I noticed Mazelcop seemed to be leaving ore under their radars untouched until late in the game. I had to assume that ore was trapped, and Mazelcop could save it until later, cleaning up some fast easy ore at the end. Also I noticed that SlyB seemed to be fake-requesting traps sometimes. I started doing these things too: I made my robots always pretend to request a trap on returning to HQ (but I basically never actually got a trap). Then I would remember which squares the enemy would have to assume were trapped, and I would save this “claimed” ore until later. At turn 100 I would start to harvest this easy claimed ore. This seemed to give a ~2 point rating boost when I started doing it.
I never did the fancy stuff other top players did of guessing which possibly-trapped ore was actually safe. For the most part my bot plays it safe and tries to rigorously infer all it can about the map, and then assumes any square is trapped unless it can prove that it isn’t.
Inferring which squares might be trapped
I’m not sure how other people determine which enemy robot dug on which square, but I kind of liked my method. Each stationary enemy robot could have dug on one of five squares, or not dug at all. I go through each possible combination of those squares and check whether that combination of enemy digs it is consistent with what I know about the map (basically, which holes appeared and how the ore on each square changed). I find all combinations of enemy dig locations that satisfy all the constraints. For example, if there are 3 stationary enemies I check whether the map is consistent with “enemy 1 dug left, enemy 2 dug right, and enemy 3 waited”, and each possible variant of that. This gives a list of possible dig squares for each enemy robot, and also determines whether each enemy robot must have dug or whether it could have just waited.
Any enemy robot that paused on the HQ might be carrying a trap, until they definitely dig or return to HQ. [This assumption that robots that return to HQ don’t have traps isn’t necessarily true. A few people (deathpat, for example) would get a trap, fake planting it somewhere, return to HQ as if they were delivering ore, then head out on the map again and place the trap for real. My robot wouldn’t correctly flag these traps and might accidentally dig them. I missed the trick teccles described of detecting this by looking at the opponent’s score. Fortunately I don’t think any top bot ended up doing this.]
Whenever an enemy that might be carrying a trap possibly digs on a square, I write down the turn number.
As teccles described, by looking at my item ids, I can determine some turns on which the enemy definitely did not place any items. For example if I placed radar #11 on turn 5 and radar #12 on turn 7, the enemy did not place anything on turn 6 (I couldn’t tell from the referee code if I can infer anything about turns 5 or 7).
So, a square might be trapped if it was possibly dug by a possibly-item-carrying enemy on a turn when I can’t rule out that the enemy placed an item.
Also: If I dig a square and survive (or if a non-item-carrying enemy definitely digs a square), it’s not trapped. To rule out traps, I encourage my robots to dig possibly-trapped squares when an enemy is adjacent to that square.
Also: if a non-item-carrying enemy digs a square, it’s not trapped
Every turn, each miner assigns each ore spot a “cost”. Some components of the cost were:
- Mostly the cost is how many turns it would take to get to that spot, mine, and return to hq.
- There is an extra cost for how many turns it would take to get to the ore (since if that’s longer the enemy might mine it before we get there)
- There is a bonus (reduced cost) for mining 2- and 3-ore spots while the enemy thinks we might be holding a trap.
- There is a bonus for mining a possibly-trapped square if it would lead to a 1-for-1 trade.
- There is a huge penalty if there is not enough time left in the game to mine that square and get back to base.
I assign each miner to an ore spot such that the total cost of all the assignments is minimal (with the Hungarian algorithm, which I read about at http://timroughgarden.org/w16/l/l5.pdf)
Robots returning to HQ with ore count as miners; they just have to factor in the time to return the current ore before starting on the next one in their cost.
It’s critical not to let the enemy blow you up. There are basically three things bad things that can happen:
Your robot digs an enemy trap. This is solved by the system that tracks which squares might be trapped. I only dig them if it would be a 1-for-1 trade.
The enemy digs a trap (of either player) and it blows up two of your bots.
Then enemy places a trap on a square adjacent to where two of your robots end up, so that next turn the enemy digs the trap and gets a 2-for-1 trade.
For 2 and 3, I made a list of all the possible explosions I was worried about. Then I made sure that no two of my bots ended up in the same explosion. This was a postprocessing step after I had already decided on preliminary moves for all the bots. Basically, I assigned a possible set of moves a cost. Mostly the cost was how many potential 2-for-1 trades the moveset exposed me to. Also the cost penalized moving robots away from where they were trying to go, and favored disrupting as few robots’ plans as possible. If the initial preliminary moveset wasn’t satisfactory (because it risked 2-for-1’s) then I did a combination of random search and hill climbing to find a better moveset (meaning, a moveset with a lower cost).
I wasn’t totally satisfied with this evasion system. I think ideally instead of being a final postprocessing step it would be more tightly integrated into my mining, so that my robots could select ore targets that would naturally lead to them not being near enough to each other to risk 2-for-1 trades.
Thanks for the great PM and congrats for your insane run
I don’t know if these stats come from CGStats or if you tracked the results during the rerun, but as I said on Discord yesterday, CG doesn’t keep more than 500 games in the last battles. It means that by the end of the rerun, some games were already missing in the history. Furthermore if you look at today’s stats, more games have been purged (186-ish remain atm, giving you 13 / 20 / 5 against Karliso). Having actual rerun stats would require to track games as they get purged.
Ah, right. Yes, my stats were from CGStats, so they are wrong for the reasons you give. So this problem is not as bad as I thought, and perhaps non-existent. Thanks!
If i recall correctly, the final rerun is 320 games for each players in legend league. So you’ll play an average of 640 games during a rerun for a 1v1 game.
Thanks. I was look at 500 of those, so the sample size was similar to what I saw. But I recall the early games being rather worse than the later ones for me, so I’m pretty sure that in this case, the overall result is statistically significant.
Finished 1266 this time. I just wanted to say I am really happy that I participated in this contest. As someone who changed the workplace 1 month ago, I needed something to boost my confidence. As you see, my rank wasn’t high enough, but after reading a lot of your comments it is a big relief that I actually did a lot of similar logic like people in the top 20. I just didn’t have enough time to prepare all the problematic cases.
Thank you, everyone, who participated and enjoyed as I did, and I hope we meet again in the next tournament of power. <3
Hey, couldnt really go tryhard this time because of canadian Thanksgiving meaning I was away from Friday till the end. So I couldnt finish my refactoring (well it was done but all bugged ) so I pretty much submitted my tuesday code. I finished 200 something. Was still a fun game and really wish it will end up as a multiplayer (hey Amadeus wink wink WINK WINKing your way).
The goal of my refactoring was:
- each bot determine the “5 best objectives” via heuristic
- A small sim determine how many turns it would take to “complete” it.
- Go through all those and check “conflicts” between bots (ex: dont mine the same cell with only 1 ore), determine the bot who actually do it based on the sim result.
- “Losing” bots go to their next best objective until every one is happy or reached there fifth objective.
It was working OK, but there was a couple of bugs where a bot would keep switching objective, making it really not optimal and I didnt have time to really use the system further with new stuff. My main goal was to compute the proper moment when a bot would end up without a proper objective and try to pop radar before that. Sadly didnt have time to implement that on top of the bugs. So yeah, make it a multi !!!
Hello, I was ranked 150 and used python. My overall strategy was not smart, used a lot of ifs and very few smart functions. Here are some highlights :
- At the start of the turn, I assign classes to each robot. At most one scout, one sapper (will carry a trap) and the rest will be harvesters
- Scouts go find a location to maximize the potential ore available, divided by the distance to the HQ (I avoid places with potential traps) while minimizing the distance to the robot
- Sapper go plant a trap, preferably, close to other traps, in existing holes, close to the robot but not too close to the HQ
- Harvesters go to their assignment following Gale-Shapley algorithm, first towards appearing ore, then towards ore that I know is there but disappeared, and finally towards locations I don’t know. If no target and a radar will be dropped, go to that location.
- If I can trigger a mine to kill more enemies bots than ally bots, go for it
- Keep assignment from one round to the other if it is not dangerous
- We need a new radar if there are less than 20 ore on the map
- Detect trap and radar dropping, don’t try to differentiate one from the other. You can dig a trap if there is a neighboring robot
- If there is an ore appearing on the edge of my vision, consider that the neighboring tiles are on a cluster, and make them prioritary for radars
- Do some random delays to simulate inserting a trap
- Pathfinding among one of the possible dest at distance 4, without too much effort
All in all I’d rewrite it if I had the time to try to find some more consistent formulas, but in the end, there was no time and ranking was so unstable that there was no way to know if a changed algorithm would do better or not…
Thanks for this challenge, it was fun !
I enjoyed playing the game much more than I thought I will. As I code only using heuristics and like strategy games, it was perfect for me.
For fun and thinking about the beginners out there, I’ve written my progression during the contest as a Twitter thread
I also wanted to try streaming but I’ve ran into two issues: I don’t have fiber so quality was meh and I had family at home. I’ll try another time (I sometimes stream some HearthStone games: my Twitch channel)
Reach for Legend
I ended at rank 125 after briefly reaching top 100 during the weekend. I tried to push for Legend on Sunday but I was finally unable to.
For the whole week, I focused on building a “simple” farming bot and see how high I could rank (the famous strategy of @Bob14). The thing I missed to get to Legend was to be able to avoid kamikazes robots making a 2-for-1 trade. I didn’t find an easy way to do the post-processing of my actions to avoid grouping together (my if-else structure didn’t help) so I didn’t even try coding it. Instead I tried a somewhat crazy strat which I believe wasn’t discussed here.
The “crazy” strat
Instead of preventing my bots from grouping together, I thought I could get ahead of the enemy and detonate their traps before the double kills happen. I was already detonating traps if I could hope for at least a 1-1 exchange but this was mainly incidental in my farming strategy.
So I quickly added a new behavior for 2 of my robots at maximum: chase the enemies with items as soon as they left the HQ with this message “like a dog chasing cars”. As soon as they stopped to dig a hole, I would join them and digged the “risky” hole with this message: “yolo”. This way, I could:
- destroy radars. I thought some bots wouldn’t manage it well and it will be a positive trade for me.
- detonate traps as soon as they’re set and go for 1-1 trade instead of 2-1 later in the game.
It worked ok. Funnily enough, it ranked in the same area (top 100 gold) as my previous farming AI. I was avoiding many kamikazes but I was now losing more farming games. Still, it was very fun to see traps go off everywhere in the first 50 turns and some games finishing as a 1v1 for 150 turns and some other games finishing before turn 40
I think this strategy could work better in gold. The issues with my implementation were the following:
- I coded it very quickly, so chasers could switch from one turn to another messing with my farming strat. I also needed some improvements in my tagging of holes as “risky” to be sure to dig the correct hole.
- I was still losing to AIs keeping their traps until the last moment.
- Also, as before, I couldn’t handle my robots as a whole and the chasing will sometimes just end up in the middle of my farmers. Ineffective and risky.
I tried to submit back my farming AI on Monday morning (since the gold boss was a farmer and I was winning against it quite often in the IDE) but I couldn’t get in the top 30 gold.
Fun game, nice viewer, interesting strategies, nice people on the chat, 15+ hours coding/watching replays/submits , what a week! Thank you all and congrats to the winners!
Hey, I was ranked 71 in the end and it was my first legend in my 12th contest (got the Happy Birthday award for this). The one big thing missing for a better ranking was avoiding 1-for-2 suicide kills from the opponent which cost me many many games in legend (and even in gold league before). I had this on my todo list for days but it would have been lots of effort as I didn’t even have any pathfinding. And there always was a smaller thing that I could implement first.
My last idea was an (what I thought) undetectable trap. I got a trap at the HQ then moved to the first empty hole, waited one turn while keeping the trap, moved back to HQ without pausing there and then plant the trap. I was so sure that would be a nice jump in the ranking but it worked suprisingly bad… I only saw a handful replays where this was sucessful and in every other game it just wasted a lot of time. I think the main reason was that nobody “believed” me planting a trap in an empty hole (I couldn’t plant it in a hole with ore though, because you would see that the ore didn’t decrease…).
But I just read your post mortems and that teccles and others used the player’s score and even the IDs of the items to determine if traps are real just blew my mind! I never would have thought about that.
It was a very nice contest and I invested more hours than ever in it. Now that it has ended I don’t know what to do with my time I hope they are many more contests to come!
I came in 7th place. Thanks to everyone who participated! Extra thanks to the_duck for innovating the meta-game and being basically an unbeatable test target and to everyone I talked to in chat during the event. It was really a blast!
My bot kept a lot of data on the map, which was used for bomb avoidance and other decision making namely:
- At the start of the turn I determined the minimum number of times each square on the map was dug in the last turn. This was done through checking how much ore was removed from each square, and if a new hole appeared there (in case it was out of my radar range). Then I subtracted off all of the digs my own robots did in the last turn.
- Next I would iterate over every hole that still had a positive number of digs, if the number of enemy robots that could have dug there equaled the number of digs, those bots were marked as resolved and the square was cleared. If any of those bots potentially was holding a bomb, I marked that square unsafe.
- Once I ran out of enemy robots or digs my loop terminated. Since I was unable to know for sure what the enemy robots did, any unresolved enemies would have any 0 ore square near them marked as unsafe.
- Finally, any squares touched by known bomb explosions last turn were marked as safe.
I ran BFS on unsafe tiles to create “exclusion zones” that I would only permit a single one of my robots to move into at a time. These represented possible chain reactions of bombs that could wipe out multiple robots if left unchecked. This was enough to defeat the common “spawn wall” strategy, as I would only send one of my robots into spawn at a time when a wall of bombs was present, thus preventing losses to a massive chain reaction.
Mining and Movement
My mining scheme was pretty simple, I scored each ore tile based on distance from the robot, number of ore at the tile, safety, and if another ally was already going to the tile. Based on these scores I assigned my robots greedily, with the robot that had the highest score getting to choose its move first, the scores for other robots were then updated and I selected the next highest score, and so on.
My movement was even more simple, robots would just move to the closest tile to their target that wasn’t already reserved by another robot. My bot began to aggressively avoid enemy bots / exclusion zones when I had 3 or fewer robots, which helped a lot against aggressive traders like SlyB and Khao.
I unfortunately didn’t realize the id abuse that a lot of the other top competitors apparently ended up using, but I think I came up with something almost as good. At the high level you needed either much more efficient mining than your opponent or greedy cache stealing (where you would take ore from places that your opponent placed a fake bomb) in order to win. My cache stealing strategy was based on the following two observations:
- Given the choice between a bomb and a radar, almost every top bot would pick the radar 100% of the time.
- Given the choice between getting a radar and not getting a radar and spending a turn idle to fake a bomb, almost every top bot would take the radar 100% of the time.
With these two observations in hand I was able to very accurately predict my opponents radar cooldown, and thus which of their robots were carrying radars. Any “fake trap” that was placed by my opponent under these conditions was most likely a radar and could be safely dug from. This strategy could easily be tricked by sometimes picking up a trap when a radar was available, but I very rarely experienced losses from this as my two observations turned out to be very reliably true.
My anti cache stealing strategy was as follows:
A small % of the time, my bot would randomly acquire a trap while in spawn (taking precedence over radars to make my bot immune to my own radar strategy were someone else to implement it). Every time my opponent greedily stole from one of my safe fake trap squares, I increased the odds of placing a random trap. As a result of these random mines peppered in, I scored a lot of wins vs the more greedy miners (including winning about 20% of the time vs karliso and about 30% vs teccles).
That is about it for my bot! Once again thanks to everyone for playing and thanks to Codingame and Amadeus for hosting this fun event!
Hi there ! I am ranked 341 after reached the top gold… Don’t test things at the last minute childs.
Before all, a great thanks to the CodinGame and Amadeus teams for this funny contest. And of course, GG to all the participants !
Civilities done, let’s talk code and strategy !
Wood to Bronze : The mower
After have created my class model and stored the data in it, I decided to start simple by avoiding using traps and radars…
But without radars there’s not a lot of possibilities, so I just coded the following :
- If there’s no hole in x+1, digging that cell.
- If there’s a hole in x+1, moving in x+1.
- If the robot carry ore, going back to the base.
This was called the mower strategy on the fr chan, and was sufficient to pass Bronze.
Bronze to Silver : I see everything !
Now in Bronze, it was time to consider to put some radars.
Don’t knowing the distribution of the ores, I simply started by putting my first one at the center of the map. For the next ones, I don’t wanted just hardcode the positions, so I created a function to find the best next position, which worked with a system of floodfills.
I now had this :
- The nearest robot of the base is the scout. It request a radar, place it, and restart.
- The others robots are the miners, they dig the closest ore, and take it to the base.
- If there’s no more ore to dig, the miners use my wood “strategy”.
That was not enough to pass Silver, so I added some stuffs :
- Decreasing the ore count on a cell when I send a robot to dig it, to avoid sending too many robots on a cell.
- Avoiding to dig the “dangerous cells”. To do that I consider the robots which stay at the base during one turn as “bombers”. Then, when the bomber stop again during one turn, I consider as dangerous the cell where it is and the adjacent ones if there’s a hole on them. I stop then considering the robot as a bomber.
Silver to Gold : Blow up them all !
My first modification in Silver concerned my trap detection, which became inefficient in front of the "fake traps", since I stopped to consider a Robot as a bomber when it make a one turn stop. So I changed that by using the opponent score : I stopped to suspect a robot only if it get back to the base increasing the score, which mean it carry an ore and not a bomb.
This was not enough to reach Gold.
So I started to consider to put traps. But I had no idea how to do that efficiently, and without waste precious farming time. Then, I had an idea : Why don’t just use the opponent traps ?
- I already knew the possible positions of this traps.
- I coded a function which calculated the chain of explosion from a trap, considering each "dangerous" cell as a trap. With it, I determined the worst losses for me if I trigger this trap, and the minimum losses for the opponent.
- Before to move each of my robots, I executed this function on each of its 5 accessible cells. If for one of this cells, my worst losses were lower than the opponent minimum losses, I digged that cell.
This was very efficient ! Blowing up groups of enemy robots and breaking radars ! I passed to Gold directly.
Gold to nowhere : The long way to the top
- Once in Gold, I noticed that more and more players stopped to use trap, which was annoying for my last implemented feature. So I decided to put just one trap, at the start, next to the base.
- I also noticed that a lot of players built traps walls next to the base. So I modified my start like this :
- Turn 1 : The scout request a radar and all the miners request a trap.
- Turn 2 : The scout go put the radar in place and all the miners dig in x=1, to deceive the enemy traps detection.
- I won some places, but my radars placement was not optimal and penalized me. So after some tests, I simply hardcoded the first eight radars positions, and kept my old function for the late game.
- A new problem then appeared : The radars destruction. My bot obstinately replaced the destroyed ones, even if the zone didn’t have interest. So I saved the ore count on the cells previously covered, and used it to determine if it was useful to replace the radar.
- The next problem was the false positives in my traps detection. So I affined it a bit :
- I keep a two turns historic of the map to detect a decreasing of the ore on a cell next to a bomber.
- I remove the “dangerous” flag of a cell if a non-bomber dig it and there’s no bomber around.
- Then, I was too vulnerable to the AIs with an "opportunistic kamikaze" strategy like mine, due to the tendency of my robots to dig the same cell. So when I send one of them dig a cell, I set the ore count of this cell to zero instead of just decrease it. The result was a spectacularly progression of about 200 places !
- The last big change I made was to totally rewrite my decision function, to give the best order to each robot, instead of just treat them in the inputs order. Here again, a big progression of about 100 places.
I was then 48th Gold.
The fall : Such is taken who thought to take…
In the top of the Gold league I encountered the following problem : The players didn’t puts traps, and my poor bot wasted a lot of time trying to trigger explosions. But, by deactivating this feature, I didn’t even reach the top 100… Then, I had a bad idea : Why not try to deactivate it, only when I’m sure that there’s no traps on the map ?
So, I spent the last hours of the contest watching replays, parsing games data and computing statistics, with the goal to find a correct indicator of traps presence.
The first tries were disastrous. I finally managed to reach the top 200, but never higher. So in the last hour, I pushed again my previous version… And never passed the 200 again.
Thanks again for this contest ! See you !
Hey, I post this a little late but wanted to share my experience of this contest, I ended 5th. It was a very nice one and I thank CG and Amadeus for this.
What I liked in this challenge were:
- A very beautiful viewer.
- A very nice problem to handle multi-agent and global optimization.
- The management of unknown information.
- No need for big deep computation, letting python a relevant language.
- A large choice of strategies.
- The Amadeus sponsor, co-organizing and prizing really stimulating.
- That I finish this contest with still so many ideas to implement
My little regrets were:
- The wood league and the first boss that did not help to apprehend the problem step by step as in previous contests.
- The point on the incrementing item ID that deserved to be clearer in rules.
- On the viewer, a different aspect for blue and red radar/trap would have been wonderful.
I was not able to code on the first week-end on this contest. I just did a fast mower bot to beat wood boss on the Sunday night. I was 800th when I went to bed. I felt really delayed when I followed discussion on forum. I was working on day all the week and had so much to code…
From this, every night I achieved to divide by 2 my rank, this remains true until the end. Don’t doubt it, with two more days I would have been first
The original point in this game is that one had to manage a lot of unknown information. The most important of it was to presume where the opponent’s traps are. Indeed, having a robot less before end game was a guaranteed defeat. So the idea was to track any robot that potentially had a trap and tag any position that could be trapped. Above that, players also use a kamikaze robot to trade for 2 opponent robots, so people were force to split enough the robot near potential traps. Nevertheless, playing too safe meant having lot of false positives on potential traps, and thus a reduced efficiency on harvesting. Using the mistrust of your opponent let you fake trap on early crystals to “book” them for end game.
From that people turns their AI into different strategy stereotypes:
- The “trapper”: place as much trap as possible and look for kamikaze trade.
- The “stacker”: stacks traps near base to make a chain detonating giving a good trade.
- The “tricker”: makes a tricky move to disrupt opponent mine tracking.
- The “safe havester”: uses no trap but just harvest with the maximum safety.
- The “unsafe harvester”: just assumes the other has no trap.
- The “faker”: no trap but books a lot of crystals for end game.
- The “sniper”: tries to predict opponent’s robot moves to snipe groups or the crystal carriers.
Every one of these stereotypes is good versus some but countered by several ones. The funny thing was that some levels of the ranking were full of a stereotype population. I remember that I remained stucked a moment in a group of stackers, as I just never predict correctly chain detonating.
To enter legend league, I think the important point was to subtly balance the AI between these stereotypes. The best would be to adapt to the opponent’s strategy, but how to know if his traps are true? Once you lost a robot, it is too late! I indeed looked for beating the most possible strategies and never tried to specialize too much (that have been my mistake in some previous CG contest).
At the end, what helped me to enter top 5 was the use of the item ID. I feel uncomfortable on this because I don’t understand how it was supposed to be used . The rules specify every item has a different ID but without details. The fact it increments a shared counter between red and blue when the item is dropped is not explained. I don’t master enough java, but in the source, I thought I understand that it was based on a hash map and that the order of the dropped items of the turn were unpredictable.
Anyway I used it to count the number of opponent’s item and I count on the other side the number of radars he put if he requested one every time he could. If there is no difference after 100 turns, I decided that my opponent was a “faker” and had no trap. This worked well against the rank 5th to 10th but the top 5 usually put at least one trap that seems essential to me.
Congratulations to the winners and many thanks to all participants. I really hope to see this game on CG soon !