# frozen_string_literal: truemoduleCwCardUtilsclassDeckComparatorattr_reader:deck_a,:deck_b,:analysisWEIGHTS={archetype: 0.30,curve: 0.25,synergy: 0.25,interaction: 0.20,}.freezedefinitialize(deck_a,deck_b)@deck_a=deck_a@deck_b=deck_b@analysis={}enddefcompareresults={on_play: matchup_scenario(on_play: true),on_draw: matchup_scenario(on_play: false),}@analysis=resultsendprivatedefmatchup_scenario(on_play:)a_info=analyze_deck(deck_a,on_play: on_play)b_info=analyze_deck(deck_b,on_play: !on_play)win_rate_a=weighted_score(a_info,b_info)win_rate_b=(1.0-win_rate_a).round(3){archetype_a: a_info[:archetype],archetype_b: b_info[:archetype],win_rate_a: win_rate_a,win_rate_b: win_rate_b,favored: ifwin_rate_a>win_rate_b"Deck A"else((win_rate_b>win_rate_a)?"Deck B":"Even")end,interaction_overlap: compare_interaction_density(a_info,b_info),synergy_a: a_info[:synergy_hit_rate],synergy_b: b_info[:synergy_hit_rate],notes: generate_notes(a_info,b_info,on_play: on_play),}enddefanalyze_deck(deck,on_play:)detector=CwCardUtils::DecklistParser::ArchetypeDetector.new(deck)archetype=detector.detectcurve=deck.normalized_curveearly=((curve[0]||0)+(curve[1]||0)).round(2)mid=((curve[2]||0)+(curve[3]||0)).round(2)late=((curve[4]||0)+(curve[5]||0)+(curve[6]||0)+(curve[7]||0)).round(2)synergy_hit_rate=calc_synergy_hit_rate(deck,on_play: on_play){archetype: archetype,color: detector.colors,early_curve: early,mid_curve: mid,late_curve: late,tag_ratios: detector.tag_ratios,tag_counts: detector.tag_counts,synergy_hit_rate: synergy_hit_rate,tribe: deck.tribe,format: deck.format,}enddefcalc_synergy_hit_rate(deck,on_play:)synergy_pairs=extract_synergy_pairs(deck)return0ifsynergy_pairs.empty?synergy_checker=CwCardUtils::SynergyProbability.new(deck,deck_size: deck.main.map(&:count).sum)probs=synergy_pairs.mapdo|pair|synergy_checker.prob_combo(pair,draws_by_turn(5,on_play: on_play))end(probs.sum/probs.size).round(2)enddefextract_synergy_pairs(deck)synergy_cards=deck.main.selectdo|card|card.tags.intersect?([:synergistic_finisher,:tribal_synergy,:scaling_threat])endreturn[]ifsynergy_cards.size<2synergy_cards.map(&:name).combination(2).to_aenddefdraws_by_turn(turn,on_play:)on_play?(7+(turn-1)):(7+turn)enddefweighted_score(a,b)# Archetype advantagearchetype_score=casepredict_matchup(a,b)when"Deck A (Aggro)","Deck A (Faster curve)","Deck A (Synergy)"then1.0when"Deck B (Aggro)","Deck B (Faster curve)","Deck B (Synergy)"then0.0else0.5end# Curve advantagecurve_score=ifa[:early_curve]>b[:early_curve]+0.151.0elsifb[:early_curve]>a[:early_curve]+0.150.0else0.5end# Synergy advantagesynergy_score=ifa[:synergy_hit_rate]>b[:synergy_hit_rate]+0.11.0elsifb[:synergy_hit_rate]>a[:synergy_hit_rate]+0.10.0else0.5end# Interaction advantageinteraction_score=ifa[:tag_ratios][:interaction].to_f>b[:tag_ratios][:interaction].to_f+0.051.0elsifb[:tag_ratios][:interaction].to_f>a[:tag_ratios][:interaction].to_f+0.050.0else0.5end(archetype_score*WEIGHTS[:archetype]+curve_score*WEIGHTS[:curve]+synergy_score*WEIGHTS[:synergy]+interaction_score*WEIGHTS[:interaction]).round(3)enddefpredict_matchup(a,b)return"Deck A (Aggro)"ifis_aggro?(a)&&is_control?(b)return"Deck B (Aggro)"ifis_aggro?(b)&&is_control?(a)return"Deck A (Faster curve)"ifa[:early_curve]>b[:early_curve]+0.15return"Deck B (Faster curve)"ifb[:early_curve]>a[:early_curve]+0.15return"Deck A (Synergy)"ifa[:synergy_hit_rate]>b[:synergy_hit_rate]+0.1return"Deck B (Synergy)"ifb[:synergy_hit_rate]>a[:synergy_hit_rate]+0.1"Even Matchup"enddefis_aggro?(info)info[:archetype].downcase.include?("aggro")||info[:early_curve]>0.5enddefis_control?(info)info[:archetype].downcase.include?("control")||info[:late_curve]>0.4enddefcompare_interaction_density(a,b)a_interact=a[:tag_ratios][:interaction].to_f.round(2)b_interact=b[:tag_ratios][:interaction].to_f.round(2)if(a_interact-b_interact).abs<0.05"Similar density"elsifa_interact>b_interact"Deck A has more removal"else"Deck B has more removal"endenddefgenerate_notes(a,b,on_play:)notes=[]notes<<(on_play?"Deck A is on the play":"Deck B is on the play")ifa[:tribe]&&b[:tribe]notes<<"Both decks are tribal: #{a[:tribe].capitalize} vs #{b[:tribe].capitalize}"endifa[:format]!=b[:format]notes<<"Decks are from different formats: #{a[:format]} vs #{b[:format]}"endifa[:color]==b[:color]notes<<"Color overlap may result in symmetrical strategies"endnotes<<"Deck A synergy hit rate: #{(a[:synergy_hit_rate]*100).round}%"notes<<"Deck B synergy hit rate: #{(b[:synergy_hit_rate]*100).round}%"notesendendend