Legends of Code & Magic (CC05) - Feedback & Strategies

:card_box: The topic where you can tell how you liked the challenge, & how you solved it. :card_box:

Thanks again aCat, radekmie for this contest. :heart_eyes:

Also, you can take a look to this post to improve the game for the multiplayer version.


BlitzProg, ~215th (3rd PHP)

Very interesting challenge, And I enjoyed the new format. Sprint was very messy however, the servers in Codingame were just too slow to fully appreciate that speedcoding. I hope it will be better next time!

The rules were easy to implement into a simulator, but you don’t know what the opponent cards nor what he drafted, and you can’t even know for sure what he decided to do during his turn. So random was very present in the games and it can be very hard to see why your AI is better or worse than another.

I’m starting to wish we could have some option to play many games instead of just one, to get a better idea on how it would perform in the arena. Perhaps limiting that option to once every 5-10 minutes, or a cooldown depending on how many games you wanted to be played.

Wood 3 to bronze (sprint): Simple picking evaluation with basic loop for the playing phase.

  • Progressively went from wood 3 to bronze by implementing the rulesets. half of that 4h contest was just a waiting game, nervously hopping I’d make it past the boss.

  • I iteratively look at my cards in hand and see if I can summon. Then I iteratively look at my summoned creatures and attack the opponent, or whichever creature is blocking me from doing so first.

Mid bronze to mid silver : Bruteforce.

  • If a creature can be summoned, do it on a copy of the game state, then recursively call the bruteforce function.

  • If a creature can attack another creature, do it on a copy of the game state, then recursively call the bruteforce function.

  • Usually all states are tested, but on a busy board I must escape early to avoid time outs.

Mid silver to mid gold : Bruteforce with improved evaluation.

  • Seeing me struggling, egaetan took a look at my evaluation functions and suggested some modifications. I could possibly have finished in the silver league without that, which never happened before, so thanks for that!

  • I no longer over-focus on the opponent HP, And do best to eliminate its attack power first to keep control of the board.

Quite some work went into making a C++ Monte Carlo with all of the rules I didn’t code in my PHP bruteforce. Well, for my first time in a contest, going C++ for a MC did not help me at all and performed increasingly worse as I completed the simulator. It did a fairly good job against most of the leading players, including the gold boss, but was consistently destroyed by the lower half of the league and rocked bottom in the arena for a reason I unfortunately could not invest enough time to understand.
The PHP bruteforce with incomplete simulator consistently performed better. How weird!

I hope i’ll have more time and motivation to face it next time. Thanks for the challenge. I’ll investigate and see what I did wrong, maybe I’ll update after that. :slight_smile:


I just want to say i was really happy when i saw this game on CG.

But now i am thinking back to playing Hearthstone with Hearthranger and that was actually fun. This feels like a downgrade to that experience.

Here’s what made this game needlessly more complicated and harder than a real card game:

  • Incomplete info during turns was annoying. I want to know what the enemy did during his turn.
  • Draft was kind of pointless since normally everyone uses an online tool to get a nearly perfect arena deck drafted.
  • There’s no coin for player 2.

Wish i played Eternal or Hearthstone instead. Seems more productive.

But thanks for trying aCat and radekmie, i appreciate the effort.

The most annoying aspect of the contest wasn’t part of the game itself, it’s the random ladder system, which was annoyingly random in every single contest i participated in, including Wondev Woman. It’s really frustrating to not know where your bot is at on the ladder skillwise.

Think it’s best to just do solo optimization puzzles meanwhile, they give a clear score on how much you improved. This ladder system for multis needs improvement.

A few useful links that could help you understand more about the theory of what works and what doesn’t:

Now that you got the basics of drafting covered you should go and watch some hearthstone gameplays on twitch or play Hearthstone yourself to better understand good trades vs bad trades and when to go face.


Nobody reads the blog or what :cold_sweat:

I will finish around 70th. I think.

My code is pretty simple:

  • I copy/pasted the draft of ClosestAI
  • I use a ISMCTS algorithm to play. It’s a MCTS but i randomize the things i don’t know. I randomize the cards in my deck, i randomize the opponent hand and his deck. Each iteration you randomize again. I stop after 2 turns (my turn and the opponent turn). My evalution function is simple too (just a scoring function for the creatures on the board, my life and the opponent life).

I didn’t spend much time on this contest, despite the 4 week marathon.

Goods points:

  • Clear viewer.
  • Very good handling of the viewer frames. Congratulation on this one.
  • Simple engine. Despite many differents mechanics.

Bad points:

  • The same problem as botg. We take a good game for humans (debatable) and we don’t ask us if the game will be interesting for an AI. In the end, there’s no big room for improving the play during the game itself. So we focus on the draft.
  • Random, random, random, random everywhere. God … 80% of draw in the ranking because of this. Very bad.
  • I think some players will bring this subject on the table. It was pretty easy to “steal” the draft of someone. This encourage AI hiding.
  • In every game with choices (botg, CR …), we always have a balance problem. Balancing a game like this take months (if not years) to be at least playable. Of course we won’t have a balance game for a community contest. Lethal was far too strong. Lethal everywhere. Lethal always.

Perhaps that came out a bit negative. In reality it was more distracting than frustrating, since we were all affected. It just shows the contest is a good idea and I hope to see more of it. :relaxed:

I’ve experienced a popularity hug myself with one of my projects and it was overwhelming, so who am I to judge anyway! Stats going nuts, MySQL database collapsing, and everyone complaining in the comments asking why their profile wasn’t working anymore.


This was my first contest. I just joined CodinGame, never coded like this (electrical engineering background) and first considered myself to inexperienced to do something like this. But I was convinced over chat to do it anyway, and wasn’t disappointed! The introduction (wood) was very good for me as a starter. I started to really like the game. I don’t think my wife nor my boss liked it through :laughing: I spend at least 2 hours per day on it.

I started by trying to find a single strategy: first summon, then attack. With that first the guards, then other cards, then face. Just one attempt of cards in order. Soon I found out that was limited, so I wanted to do all permutations. But got time-outs! I realized why when I did the maths. So I decided I would do a number of random order attempts and chose the best. I was quite proud of that system, until I realized many were doing that and it’s called Monte Carlo. In a second phase I also did a Monte Carlo for the enemy with the same algo: for every set of moves I simulated 150 random ememy moves and chose the best. I could then sim about 3000(sparse table)-500(full table) attempts in total.

I tried to find a good draft, but just couldn’t figure out a good algorithm, and it turned out some cards were just better then others. Most people that copied ClosetAI’s draft ended up top. Same for me. I modified it a bit, and added a slight mana curve in the end, but still a hard-coded order.

The end weeks were all about scoring: how to determine which MC sim is the best. I had a simple algo: card attack+defense+some function of the abilities. It turned out all focus was on Guard(4x), Lethal(2x) and Ward(4x) on my side and Lethal (4x) and Ward(4x) on enemy side. At first I also scored player and opponent health, but removing that greatly improved my bot’s performance. Focusing on the cards first turned out to be the best. But in the last 7 days I just could not find a better scoring algo.

In the end I’m quite proud of my achievements I even was 14th in legend for a short time. If I look at the sims now I’m 30th and dropping. Oh well. I enjoyed it. Thank you aCat and Radekmie

edit: my scoring code

int Score(const int sourcePlayerIdx) {
    int score = 0;
    int targetPlayerIdx = 1 - sourcePlayerIdx;
    Player& sourcePlayer = players[sourcePlayerIdx];

    while (sourcePlayer.health < sourcePlayer.rune) {
        sourcePlayer.rune -= 5;
    if (sourcePlayer.deck < sourcePlayer.cardDraw)
        score -= sourcePlayer.cardDraw;
    else if ((tables[sourcePlayerIdx].size() + sourcePlayer.cardDraw) <= 8)
        score += sourcePlayer.cardDraw;

    for (const int cardIdx : tables[sourcePlayerIdx]) {
        const Card& card = allCards[cardIdx];
        score += (
            + card.defense
            +((card.abilities & Card::Abilities::Guard) >> 1) /* x4 */
            + ((card.abilities & Card::Abilities::Lethal) >> 3) /* x2 */
            + ((card.abilities & Card::Abilities::Ward) >> 3) /* x4 */

    for (const int cardIdx : tables[targetPlayerIdx]) {
        const Card& card = allCards[cardIdx];
        score -= (
            + card.defense
            +((card.abilities & Card::Abilities::Lethal) >> 2) /* x4 */
            + ((card.abilities & Card::Abilities::Ward) >> 3) /* x4 */
            + 1 // bonus for every card

    if (players[targetPlayerIdx].health <= 0) score += 10000000; // end game!

    return score;

weird, eh?


I’ll more likely finish in the bottom of Legend, with Javascript

I actually enjoyed the subject of the contest as I never played HS a lot but still appreciate the mechanics even if some things are different.

This was also the first contest I really spent a lot of time on (after GitC) and also the first time I implemented a sim system that must be basic compared to my Legend mates. As a noob in that category, I started by rewriting the referee system and then I was kinda hesitating on how to do it. I was about to do a generation+permutation system when reCurse stream went on in which he explained MC principles (thanks god, it was very interesting, thank you !).

Implementing the MC with rather simple coeff within the eval bumped me from bronze to gold quite quickly without much change. I went for a very pessimistic eval as I gave the enemy score bigger bonus than I did for me. Otherwise, I thought that Lethal was pretty nice so I tried to go for it as much as I could, throwing D&B away.

I had troubles going past +150 in Gold as my draft wasn’t good enough to climb the ladder (although the boss was pretty much 60% win). I changed from manacurve to “on-the-air” score calculations but it didn’t go well as I was still stucked around +100. As I could not find any better way to improve my draft (probably because i’m not that clever) I copied ClosetAI draft too and this really helped my deck overall. Some minor changes on the eval made it and my AI went straight to Legend.

As the schedule was not that good for me, I tried some changes there and there but didn’t find the time to do it well. I actually did not touch my program for the last week but I probably would not have done any better. I guess that simulating the opponent turn with board content could have been a good way to go to avoid stupid losses but I think it could have been even more interesting if we had info on enemy draft picks.

This is the first time I reached Gold and above so I’m pretty happy about it but I’m still a bit disappointed about the fact that I had to emulate ClosetAI draft to improve my score, so I probably don’t deserve it.

Anyway, thank you aCat and Radekmie for this contest which was quite cool and very interesting :slight_smile: !

TLDR : MC, pessimistic eval, manacurve then (ClosetAI) draft, boom : Legend

Sorry for bad englado


Vry, ~294th (206/299 Gold)

Sorry for my English, i will update my post later … :smiley:

Bronze to Silver :

Implemented a simulator in two hours (no optimization) during first week, but it stayed unused … it was time for holidays ! :stuck_out_tongue:

My bot decided to leave Bronze league by himself during my holidays … and slept 17 days in Silver !

Silver to Gold :

Last Wednesday, i add MC to use my simulator !

Main loop :

while (1)
  // Try 1k rollout
  for (i=0; i<1024; i++)
    memcpy(&(ng), g, sizeof(GAME));
    rollout(&(ng), 0);
    double e = eval(&(ng));
    if (e > best)
      best = e;
  // End ?

For one move :

int rollout(GAME* g, int depth)
  // LegalActions
  me->c_action = 0;
  computeLegalSummons(me);        // See referee
  computeLegalAttacks(me, enemy); // See referee
  computeLegalItems(me, enemy);   // See referee
  // End ?
  if (me->c_action == 0) return;

  // Pick one move
  int m = (fast_rand() * me->c_action) >> 15; // aka % me->c_action

  // Copy action
  ACTION* a = &(me->t_action[m]);

  // Execute action
  int result = advanceState(me, enemy, a); // See referee

  // Continue
  if (result == 0) rollout(g, depth+1);

Without an evaluation function my MC is useless … and i have no idea on how to do it !

I finally use player’s health, card’s attack/defense and abilities (with 9 magic numbers …).
I chose magic numbers randomly (not enough time to think seriously about it) … here is the best i found :


  • 0.37 x health

For each creature on board:

  • 1.00 x attack
  • 0.71 x defense
  • 0.69 x attack if hasBreakthrough
  • 0.49 x attack if hasCharge
  • 0.49 x attack if hasDrain
  • 0.56 x attack if hasGuard
  • 0.37 x attack if hasLethal
  • 0.76 x attack if hasWard

To be continued …


Mid legend, second time reaching legend \o/

Preset draft values and DFS for me + opponent moves approximation (it performed better than using a full sim for the opponent), more details in this post mortem.

Thank you @[LOCM]aCat, @Radekmie and CG team, it was fun and I learned new stuff :slight_smile:


45th in legend.

I used a neural network and a simple form of reinforcement learning, see https://github.com/eiisolver/LegendsOfCodeAndMagic/blob/master/README.md

I really enjoyed this contest, thanks a lot to aCat, radekmie and codingame! 4 weeks was nice, no need to hurry.


My feedback :

  • We need a card contest, we had it : Good thing
  • separate sprint and marathon is a very good idea :slight_smile:
    but …
  • sprint was too short (you need 24h or 48h for timezone balance)
  • marathon was too long for this kind of game (random)

I hope the next game will be an abstract simple game ; long time we had one.
… and keep the sprint idea ; many people in few hours : very nice.


+1 :stuck_out_tongue:


Hi everybody,

This contest was fun and very interesting. I knew the game Magic, but not this one, so I installed it (it is free on Stream). But why everybody said the game is HearthStone. It is not. The game is Elder Scroll Legends, just look at the card below :

I Installed the game and played a while between the Sprint and the Marathon contests, and I can say that the arena mode is nearly the same game as the contest version.

I just finished #32 after one full day of watching my rank slowly decreasing.

Here is my story :

  • Heuristic only to pass Woods, Bronze, and Silver Bosses
  • Simulation + evaluation in Gold league
  • Random moves, card by card to build my turn
  • Random moves, card by card to build opponent turn
  • Very complicated evaluation
  • choose the best move which maximize my turn and minimize the opponent turn
  • between 500 to 180000 simulations
  • a bit of mana curve
  • Using draft values found somewhere during the contest, gain 30 places before Legend opening, and jump from mid Gold to top 20 Gold
  • Pushing my Bot just before the Gold Boss appears, made me 1st Gold for a small minute :smiley: ha ha
  • In Legend league, it was very difficult to find ways not to fall down in ranking :
  • mana curve was worse than no mana curve at all
  • Complicated evaluation was worse than only : 1.0 * attack + 1.0 * defense for all cards on board
  • Deep 3 simulation was worse than deep 2
  • So I worked on performance :
  • Reduce my code from 1800 lines of code to 900 (who knows ?)
  • factorize my if()
  • inline my functions
  • optimize my data structs
  • Estimate the number of possible moves on deep 1 and on deep 2 and determine a relative number of loops to simulate deep 2
  • Set a min and a max for this number of loops
  • And rage push for a week to find some new magic tuning
  • The best solution was always : “delete new magic tuning” or “where is my last well ranked silver version ?”
  • And on the last day, try hard to not repush, because the last hours, you don’t want to do that !

Very great contest, thanks to aCat and radekmie for the good job. We all had a lot of fun.


I think there isn’t enough explanation about draft phase. It seems many in legend used whatever best draft order was around and good to go.
What makes a good draft? Lethal? Guard? Some NNN/cost ratio? How prior cards are taken into account? What worked and what failed at legend level for drafting?


Ended #23 legend.
A bit disappointed, since I lost 10 places (13->23) between 17h55 and 18h05 without reasons, and couldn’t get them back.


Like many others, I had a sim.
I used an alphabeta search for two plies (only taking account enemy board, I just couldn’t make enemy hand work). It was really poorly optimised with a lot of stl vectors and 1025-character strings as hash (yup) but it still answered in less than a millisecond in 90% of cases, only breaking if the game was already over (one side having too many cards on board). No action ordering either.
An edge of my alphabeta tree is an action. As such, a max node (maximising my score) can call other max nodes (making an action and trying to make another one) so it’s not strictly alphabeta. To fin the best set of actions, I save the score of each seen state in a hash map and reconstruct the path after the alphabeta.
My eval only took into account card hp, attack, lethal and ward abilities (and a tiny bit of players health). I did not try to play around runes.

Finally for the draft, which was the most important thing at the top, I analysed replays to create them (I have ~5Go of replays on my computer). I computed winrates of cards for each side for the top10 to rank them (value between 0 and 159, not very smart) then added bonuses to fit a really large manacurve.
My draft ranking for player 0 and 1 are not that different (mean:±3), because not so many players used different drafts (thus my statistical analysis cannot learn really different drafts) but looking at the winrate of a side with n card of a given cost gave interesting results. Without surprise, blue tends to have lower cost cards and red higher but it was in a greater proportion than I expected.

Overall I have mixed feelings about my bot. My engine is exact yet slow (but faster was not required), my eval was basic, and my draft statistically computed (or stolen?). Having a 4-week contest was great, especially during summer, but the game was not deep enough for us to play with it for that long and the top capped before the opening of legend.


The game

I thought it was a fun game and if it had been a 10 day contest it would mostly be ok. The biggest problem was the asymmetry. If most games are a draw because P1 mostly wins, then you need too many games to test if a change to your bot is a real improvement. Brutaltester helps but only to some extent. I ended 5th which is somewhat on the lucky end of the error margin, but I can’t be sure of that.


I feel I mostly failed at draft. I don’t know why. It seems it is something I could be good at, but I wasn’t. I started with a formula with about 30 weights to fit a good manacurve, item/creature ratio and to pick good cards. I fiddled with it for days and only went down in rank (from top 3 to about 20). Then I was told that ClosetAI used an ordered draft with no formula, I stole his and was back to top 3. After that I changed it a bit and tried other things. One big thing I tried was this:

I kept track of cards in the 60 card-cardset and cards I already picked, then randomly finished the cardset about 500 times, randomly picked 3 choices for all remaining turns and finished my deck according to cardorder. I then calculated the chance to draw a 1, 2 or 3 cost creature-card in my first few turns. I set a probability I wanted to achieve and then did a binary search to find the best weight adjustments to 1,2,3 cost creatures to achieve this probability. This was a 500 line solution to my draft problem I was pretty proud of. It did exactly what I wanted and achieved the probability I was looking for.

Then I tested it in brutaltester and in the leaderboards and it did nothing. I would have been cool with the result, if it had been way worse, or way better, but it just did… nothing. Working on draft only only got worse from there. Every adjustment to draft failed for me and in the end I only had the ClosetAI draft with some common sense changes. Pretty frustrating. I think draft was the main weakness that separated me from the players above me in the leaderboard (and maybe some below me as well).


My search is what got me to nr 1 when legend opened and kept me in top 10 most of the time during the last 2 weeks when I made no real changes. I’m sure I have a good search. This is how it works:

I have a tree (parents, children etc.) that contains possible decisions you can make during a turn and also possible decisions the enemy can make (1 turn for both players). I have the nodes of the tree pregenerated so I dont have any garbage collection and save the decisions in an integer (like a bitboard). For example: Summon creature 1, 4 and 5 from my hand would be coded as binary 00110010. At most you can summon 8 creatures so you only need 8 bits.

I would start with the summon phase. All combinations of summons that fit on the board and mana pool will be considered. The order of summons does not matter and I am not considering duplicates. A full summon phase fits in 1 child node.

For green items I do the same thing only I space out my bits in groups of three. 3 bits can make 8 possibilities. Since there is a max of 6 creatures (+ not using = 7) this fits. There can be at max 8 green cards in your hand, so you need 3 * 8 = 24 bits. This also fits in an integer. So a full green item phase fits in 1 child node as well.

Attacking is way more complicated. A single attack node contains a creature number (0 to 5) and a target creature (0 to 5, bitshifted) . I don’t combine multiple attacks in one node because it wouldn’t fit.

Attacking is the best way to explode your solution space so you need ways to reduce the number of possibilities. My main trick is: Whenever the order does not matter, you enforce a specific order and disallow all others. The order I enforce is:

  1. Items that remove abilities go first. Since they always remove ward (with 1 exception), there is no point in using another creature or item to remove ward first. I never allow these items to be used after other kinds of items or attacks on the current target.

  2. Low attack items and creatures always go before higher attack items and creatures, except where breakthrough is involved. I allow breakthrough creatures as the last hit even if they have lower attack. Of course I do not allow them later than an even higher damage breakthrough creature. Whenever items or creatures are tied for attack, I let the higher instance id go first (or last, I don’t remember and it doesn’t matter as long as you are consistent). The main reason to let low attack creatures go first is because of breaking ward.

  3. I attack the enemy creatures from left to right. Every time I arrive at a creature I can attack it, or skip it. If I attack it, I can attack it for as long as I want or until it is dead. If the creature is damaged, I must kill it, or stop attacking entirely (my bot is not allowed to damage, then move on to different target). The idea of this is that it makes no sense to attack multiple creatures and not finish them off. Ward is an exception to this. I can remove a ward, then move on to the next creature. I can of course also damage a single creature and not do anything else.

I would do all this twice. First for guard creatures (to remove all guards) and then for non-guard, if possible. I would always hit face with whatever hadn’t attacked. All of the above unique possibilities lead to different nodes in the tree.

If my attack leads to more board space for summons, I would repeat the summon phase and allow a new green item phase (just for the new summons). I would also allow a new attack phase, if I summoned a charge creature and only if I did not already summon a non-charge creature in the first summon phase (because why summon a charge creature second if you can summon it first?). I would also give my bot the opportunity to use blue items that can be used on the enemy (at the end of turn).

Enemy turn attacks were resolved the same way (as above). The eval score was backpropagated. Enemy nodes would backpropagate the worst score and my nodes would backpropagate the best score. So every node in the tree always has the best score that is achievable on that path, for the player it belongs to. I also used a kind of alpha bèta pruning that made it so that I only needed a few more sims to do enemy attacks after solving my own turn. If my best path through my own part of the tree with the best enemy moves would still be better than my other paths, there would be no need to calculate enemy moves on the other paths (enemy moves can only reduce score). When I thought of this trick, my required simcount became roughly 20x smaller and calculation time mostly rounded down to 0 ms.

Then I figured, if I have so much calculation time left, why not try a greater depth? I did and it failed. I never found any other way to use my calculation time, unfortunately. Some part of me tells me I might have, if I had used another week for it, but I doubt it. At least my brutaltester series were fast and I guess I helped out the CG servers by keeping my turns short.

Evaluation of search

My eval is simple. I have a score for each ability and for offense and defense. Ward has synergy with offense, so I added that to my eval as well. I have a weird HP formula for me and the enemy that has a peak in it, meaning for some reason it seemed ok if my bot got more eval score for having less hp when over the peak. I guess maybe it has something to do with runes, because the peak was fitted around 28 hp (near rune 25 breakage). My effort at finding an alternate formula failed. I also evaluated card draw and I included runes and card death. The way my bot works is that it would try to get maximum score even if it found a win. So my games often finish with breakthrough damage (to kill 1 more creature), or to kill as many creatures as possible before finishing off the enemy. Somewhat amusing (and evil).


eulerscheZahl: For helping me set up his parameter fiddler and otherwise useful discussion.

Counterbalance: For helping me set up brutaltester (and for coding those new releases in the first place)

blasterpoard: For much useful discussion. He also pointed out the idea of a fixed cardorder (after which I stole one).

And of course (!) the creators of the game for making a beautiful addition to the CG arenas, because that is what it will be. It might not have been the best contest for balance reasons and because of randomness, but it is still pretty neat and will draw players I think.


Newbie question but, how do you download replays ?


I said it’s most similar to Hearthstone because it really is. Elder scrolls has the board divided into 2 and the right side board has an extra effect that grants stealth to units on their first turn, meaning they won’t be attacked and guard is also ignored.


See the funny cloud symbol on the creatures in the image above, on the right side of the field. They can’t be attacked on first turn after being played, they are basically safe.

Hearthstone has a single board where you can place cards. Yes the cards are copied directly from TESL, but they are also slightly modified. Not only this but you can find most cards on the current list in Hearthstone. Even the abilities you see on the cards can be found in hearthstone (not sure about breakthrough, havn’t played the game in a while)

The 12 mana minion isn’t found in hearthstone, since there is no creature with that many abilities in that game, but instead there are creatures with far more unique abilities instead of higher count.

Since the cards are overall very similar in all 3 games and only the battlefields are different, i call this Hearthstone, because TESL has a completely different strategies with the 2 different boards. If you lose dominance on one side you can try to push on the other side where the opponent has weaker presence until you draw a better card for example.

See in TESL the 4 mana 9 attack 1 health would be quite efficient in the “shadow lane” (right side board) where it gains stealth on first turn since you have almost guaranteed direct player damage next turn.

It’s a completely different game from the one in the contest and more complex for sure.

In an ideal card game with perfect card balance, you would play each turn a creature that is proportionally strong to its mana cost, on curve. Playing on curve means you would spend all your mana to not waste 1 mana.

So you would try to draft in such a way that you have good chances to play a 2 mana on turn 2, a 3 mana on turn 3 and so on …

In order to achieve this you go generally with a safe formula that results in good chances for drawing cards that you can play early on. Something like:
8x 2 mana creatures
6x 3 mana creatures
4x 4 mana creatures

Any high cost card you have in your hand early on is basically dead weight since you can’t use it until much later in the game and by that time the game might already be decided in your opponents favour because you didn’t have the chance to establish a decent board presence.

Mana curve mattered less in this contest since lethal is a powerful ability. The 2 mana 1/2 with lethal and guard is strong early and late game since it can remove big creatures, while a regular 2 mana cost creature wouldn’t have any chance against a big creature late game.

A strong 2 mana creature can be counted as a good 3 mana creature while it still costs only 2 mana, So this is where the mana curve starts to be less important and your 2 cost creature count and 3 cost creature count check should be less strict.

When it comes to late game cards, the ones that have an immediate impact on the board are more valuable, such as the nightmare 12 mana card. You want something that can affect the board immediately if possible.A 8 mana 8/8 with no skills can be ignored the turn it’s played by the enemy’s weaker creatures, they don’t have to trade with it, they can just hurt the face instead.
So that 8 mana 8/8 you play on turn 8, is in fact a creature you played on turn 9. Of course the opponent might take it into account if their board side doesn’t have enough damage to win the game.

If however there’s two big creatures then raw stats matter more.

Now about spells, you generally want a few low cost low damage spells to remove small creatures (maybe those annoying ones with lethal on them) and wounded big creatures. About 3 maybe?

And then you want a few big damage spells that remove the big nasty enemy creatures, such as the cheap 5 mana red spell that deals 99 damage, seriously overpowered imo.

You can treat the 4 mana 1/1 creature with lethal and charge as a damage spell and only summon it when you need it so the opponent doesn’t remove with with some cheap damage spell.

The green buff spells are good based on the picks you drafted. Bonus health on a lethal creature with small health can be great if the opponent didn’t draw his lethal cards yet.

If you miss out on a lot of 2 cost creatures because you opted for higher cost creatures with higher value during the early draft, you can could compensate by picking more red / blue damage spells later on in the draft.

When drafting, a good approach is to go by pure value the first 15-20 turns and then try to smooth out your mana curve and pick based heavily on card synergy.
So let’s say it’s turn 20 during the draft and you have a lot of low cost (2-3) creatures you know you are likely going to win easier if you rush for your opponent’s face, in which case cards with card draw effect are very useful to get faster through deck and commit more units to the board so your opponent has less chance to deal with them and pick less high cost cards if possible, maybe only 1 king nightmare at most or a big red removal spell to get rid of a guard creature that prevents you from winning. You would also pick more 4-5 cost cards as opposed to 7+ cost cards when you get the chance, because throwing a lot of lost cost creatures on the board is best backed up by mid-level creatures that you can play immediately after the first few turns and have a moderate impact on the board, instead of having to wait some extra turns to play the high cost high impact creature.

See the icy veins website for more details on how recognizing the type of deck available to you at round turn 20 in the draft and where you can go from there on. That website contains all the basic info you need imo.

One thing i considered doing but didn’t bother with due to incomplete information of opponent’s turn:
check the instance of the opponent’s cards when it had a huge effect on the board

If you keep track of all the cards during draft, then during the game when you see the instance of a card being successful you can adjust the value in your draft.

It’s like optimizing 1-2 cards draft picks based directly on your opponent’s outcome.