Let me start with saying that this was the most complex multi-player game ever attempted in CG. Iām sure that team CG was very happy to see how you pushed the new platform to its limits and from that experience it can only get better over time. Unfortunately, at the same time it was the least polished contest of them all. Partly perhaps because of the complexity of the task, but partly due to the design choices made.
I thought it will be useful to dissect these choices, and talk in detail about what and how to improve, as this might help not only you adjusting the game for multi, but hopefully would be a good lesson for future contests makers too. So this is my attempt at constructive criticism, where Iāll try not only to complain (as many people before me did :P) but also to discuss the possible solutions.
Input
My Python code was 300 lines. 100 was parsing the input. And I had to do it for the very first bot in Wood 3 already, even if I couldnāt yet buy any item or cast spells and I really didnāt need to know about creeps and the jungle bushes yet. All I needed at this point was: (x, y, hp) for every minion and hero and maybe the tower health.
More details on input, entities and attributes
Yes, tower should not be a unit and have āstunā, āshieldā or āgoldā. Also, static attributes that do not change throughout the game, like tower ārangeā shouldnāt be a part of input (same for bush āradiusā or creep āmana regenerationā). I was also really surprised that all the units (yes, including towers) had given the hero attributes, just set to zeros and ā-ā. Why? It doesnāt simplify anything. I have a type, I can tell which entity object to create. All I need to do is:
unit_type, owner, *params = input().split()
if unit_type.startswith("H"):
hero = Hero(params[0], *map(int, params[1:]))
heroes[owner].append(hero)
if unit_type.startswith("T")
tower = Tower(*map(int, params))
towers[owner] = tower
if unit_type.startswith("M")
minions = Minion(*map(int, params))
minions[owner].append(minion)
Also, it would be great if string attributes are at the beginning (or at the end) of the attribute list, so I can easily convert the rest to numbers (see the Hero constructor in the example above).
And I donāt understand why we needed to have āroundTypeā when all you have to do is:
turn = 0
team, zones, items = init()
while True:
me, enemy, creeps = read_state(team)
turn += 1
if turn == 1:
print("DOCTOR_STRANGE")
continue
if turn == 2:
print("IRONMAN")
continue
Furthermore, I think the choice of a hero in the early leagues should be fixed. Use just one, same as boss is using. Then also fix the second hero, and only introduce all 5 in bronze (or laterā¦). That would make the introduction into the contest much gentler.
I know there were several people asking for all input from the start in the past contests. But this is a bad idea. If you havenāt seen it so far, I hope you learned your lesson now. Itās much easier to start small and handle more input later when needed (and usefulā¦).
Last thing, the score (here: last hits / denies / hero kills). Already mentioned by several people. The engine should track the score and provide it as input, letting the players focus on playing the game.
Description
Description was overwhelming. But if you take away bushes, neutral units, skills, items and limit the actions to āMOVEā and āATTACK NEARESTā, and the input to heroes and minions āx, y, HPā and towers āHPā, it starts to look easyā¦ Also the initial winning condition could be simply ākill all heroes or have more goldā and you can introduce the last hits and denies only when items become available. Really, nothing is more rewarding than being able to start simple and discover more depth as you go (which links to the leagues design, more on that later).
The biggest problem though, was the description formatting. You underused lists (itemisation) except in description of the skills (where you used them), which was begging to be a table. For my own sanity I actually made one, as I got tired of not being able to compare things quickly:
It should probably be combined with the tables given in the github readme. And I donāt really mind whether it is a part of the description or external resource with ādetailed detailsā, as long as itās all in one place, and easy to compare.
The other issue was the very description of the spells. Itās confusing and inaccurate. It took me long time before I understood why my opponents are able to escape the āinstantly castedā WIRE. Surprise, surprise, it has a fly time of 0.3 and if enemy moves out of range, no hit. So itās not really instant (like Hulkās EXPLOSIVE SHIELD for example). I only found this out by reading the source codeā¦
The biggest surprise though, was that I canāt āMOVE ATTACKā a predicted position of a charging hero while retreating. Simply, if I finish the move faster, I will fire out of range, while the other hero will still be āgetting thereā. The only way to attack in this turn was to move forward with āATTACKā (bad idea when your goal is to retreat). That was really non-obvious, and it put an end to my 2 * melee sneaky strategy that (I thought) was my ticket to legendā¦ So yeah, such side effects of the complex rules should either be explained, or rather avoided if possible (see more on that below).
Actions
I really liked the game concept. Co-op play with multiple objectives, hidden information and opponent moves prediction. All the best things my favourite contest ā CodeBusters ā had (I still remember the brilliance of Recarās ghost hurding tactic :)).
On top of that, you added a time mechanic where actions take fractions of a turn time. Itās great idea but it should be implemented differently. Instead of fixed actions with specific
ātime propertiesā, players should have the full control over time (in higher leagues). That is, be able to specify a list of actions to execute at each turn. Exactly like humans playing MOBA can do.
Why? Because āATTACKā or āATTACK NEARESTā are not that useful. I find myself doing āMOVE, ATTACKā a lot and really wishing for āATTACK, MOVEā or āMOVE, SPELL, MOVEā. Because if you think about it, there is no reason why the number of actions per turn should be limited, when you have time. Time becomes the limit, and as long as you have it, you should be able to chain actions in any order you find useful.
Gameplay visualisation
First rule of visualisation is āmore is lessā. I understand that plain geometric objects (like in reCurseās battle station) are not very exciting, but this is why we have the debug mode option. You should use it to show unit IDs, movement and attack vectors, spells and damage / health, all at ones, without a need for mouse hovering for tooltips. Everyone would love you for it
Another thing is stepping turns vs. continuous play. I understand that due to the time mechanics some events happen in the middle of a turn and looking only at the end point (which happens while youāre stepping through) doesnāt give a full picture. But I would argue that it should, to make the game analysis easier. That is, for every action regardless of the exact time, there should be a visible indicator that it has taken place this turn, at the final animation frame. So that, when I step through a game, I can still tell what happened. Evaporating damage numbers are a good anti-example here.
Also, attack and move speed are not the key stats. HP and mana should take their place rather than being written with white font over a bright barā¦ (at least use a black font). And it would be great to have a small text notifying of the effect of spells (e.g. āhit by WIREā, āshield activatedā or āpulled to (x, y)ā). It would help tracking the action and especially, identifying cases when a spell failed.
Leagues and Bosses
The key to success here, is to gradually uncover the complexity. Introduce new rules, actions and input as people advance through the leagues and write bosses that use the new things and are difficult to beat without some improvements. I remember that Wood 1 boss was extremely weak. Iāve just added a 2nd hero doing exactly the same thing as the first one and rolled over it (I wondered if 2-hero Wood 2 boss wouldnāt be stronger). It should at least challenge me to buy items or maybe even trigger root aggro so I have to stop ignoring the creeps.
Anyway, if I was designing the leagues I would do something like this:
| ACTIONS | HEROES | WIN CONDITIONS
--------------------------------------------------------------------------------
WOOD 3 | attack nearest, move | 1, no spells | kill heroes/tower or more gold
WOOD 2 | attack, move attack | 1, 1 spell | kill heroes/tower or more gold
WOOD 1 | buy, sell | 2, 1 spell | more kills (instead of more gold)
BRONZE | action chains | 2, 3 spells | last hits (instead of more kills)
SILVER | | 5, 3 spells | denies
Iām quite convinced that it would help if in lower leagues taking down the tower was easier than later (was it like that already?). And probably in general the towers should be weaker, so that they have to be defended more carefully. I would also make the creeps weaker to incentivise the jungle play and make more strategic shopping possible.
Competition
I donāt share the sentiment expressed by Magus, Agade and Neumann, that optimising a clear objective is more fun than constantly tracking the META and countering opponents strategies. I enjoy the battle of the wits a lot, even if Iām not particularly great at it
I also donāt see anything wrong with āhard codingā a behaviour to counter specific strategies. This is a multi-player game, not an optimisation puzzle, so you need to adapt to the opponents play. And the rock-paper-scissors effect in the ranking, is an evidence that it was hard to win with a single strategy. You need to do variety of things to be hard to counter, and also, constantly improve your bot, when other people challenge you. I see why reCurse didnāt like it, but when you are not in the top 10 for the entire 10 days, you see this as a chance!
I also donāt agree with Unihedron that the contest should be about programming. Itās not that we implement to specification and go home (would that be fun?). We play a game! And part of playing it, is to figure out what is going on, develop a strategy, outsmart the opponents. However, I agree that the learning curve could be more kind, but that can be solved with a better leagues design.
Personal experience
I started this contest feeling I will hate it. I donāt play MOBA and I had no strategic intuition of what should my hero do. My very simple approach just blasted me to bronze and I didnāt have time to feel the game and understand the mechanics. So I watched how other bots play, and started trying out all sort of sneaky tactics, understanding the mechanics by trial and error (and some reading of the engine code). My efforts where not extremely successful, my initial range bot was still ranked higher than all the sneakiness, but I enjoyed the process a lot!
So thank you @AntiSquid, @Illedan, @Wildum for all your efforts, staying on chat patiently answering questions, fixing bugs, trying to make everyone happy. I think you did great job and a few of the shortcomings listed above, should not overshadow that.
Note to the CG Team
Please make the engine usable in an āold refereeā mode, that is to run local games from command line. Itās a win/win situation if we can test locally, as we donāt abuse CG servers with multiple submits through greasemonkey scriptsā¦