# 206164c92a23abd549add9b9b3303f4cbfc6d9503b7293ae51a79c336810b379 Elan 2.0.0-alpha4 valid

# Wordle Solver: in 'hard' mode (every attempt must be a valid possible answer)
# Works only for the 2,309 words that may be set by the official online Wordle
# Mark each letter of program's attempt word with:
# * for in-place match (Green)
# + for out-of-place match (Yellow)
# _ (underscore) for no match (Grey) e.g.
# RAISE
# _*_+_
main
  variable possible set to allValidAnswers.split(" ")
  variable mark set to ""
  variable attempt set to "RAISE"
  while not mark.equals("*****")
    set mark to input("" + attempt)
    set possible to possibleAnswersAfterAttempt(possible, attempt, mark)
    set attempt to bestAttempt(possible)
  end while
end main

function isGreen(attempt as String, target as String, n as Int) returns Boolean
  return target[n].equals(attempt[n])
end function

function setChar(word as String, n as Int, newChar as String) returns String
  return word.subString(0, n) + newChar + word.subString(n + 1, word.length())
end function

function setAttemptIfGreen(attempt as String, target as String, n as Int) returns String
  return if(isGreen(attempt, target, n), setChar(attempt, n, "*"), attempt)
end function

function setTargetIfGreen(attempt as String, target as String, n as Int) returns String
  return if(isGreen(attempt, target, n), setChar(target, n, "."), target)
end function

function evaluateGreens(attempt as String, target as String) returns List<of String>
  return range(0, 5).reduce([attempt, target], lambda a as List<of String>, x as Int => [setAttemptIfGreen(a[0], a[1], x), setTargetIfGreen(a[0], a[1], x)])
end function

function isYellow(attempt as String, target as String, n as Int) returns Boolean
  return target.contains(attempt[n])
end function

function setAttemptIfYellow(attempt as String, target as String, n as Int) returns String
  return if(attempt[n].equals("*"), attempt, if(isYellow(attempt, target, n), setChar(attempt, n, "+"), setChar(attempt, n, "_")))
end function

function isAlreadyMarkedGreen(attempt as String, n as Int) returns Boolean
  return attempt[n].equals("*")
end function

function setTargetIfYellow(attempt as String, target as String, n as Int) returns String
  return if(isAlreadyMarkedGreen(attempt, n), target, if(isYellow(attempt, target, n), setChar(target, target.indexOf(attempt[n]), "."), target))
end function

function evaluateYellows(attempt as String, target as String) returns List<of String>
  return range(0, 5).reduce([attempt, target], lambda a as List<of String>, x as Int => [setAttemptIfYellow(a[0], a[1], x), setTargetIfYellow(a[0], a[1], x)])
end function

function markAttempt(attempt as String, target as String) returns String
  let greens be evaluateGreens(attempt, target)
  let markedAttempt be evaluateYellows(greens[0], greens[1])
  return markedAttempt[0]
end function

function possibleAnswersAfterAttempt(prior as List<of String>, attempt as String, mark as String) returns List<of String>
  return prior.filter(lambda w as String => markAttempt(attempt, w).equals(mark))
end function

function maxWordCountRemainingAfterAttempt(possAnswers as List<of String>, attempt as String) returns Int
  let d be new Dictionary<of String, Int>()
  let d2 be possAnswers.reduce(d, lambda dd as Dictionary<of String, Int>, answer as String => incrementCount(dd, answer, attempt))
  let keys be d2.keys()
  return keys.reduce(0, lambda maxSoFar as Int, mark as String => if(d2[mark] > maxSoFar, d2[mark], maxSoFar))
end function

function incrementCount(d as Dictionary<of String, Int>, possAnswer as String, attempt as String) returns Dictionary<of String, Int>
  let mark be markAttempt(attempt, possAnswer)
  let keys be d.keys()
  let count be if(keys.contains(mark), d[mark], 0)
  return d.withSet(mark, count + 1)
end function

function allRemainingWordCounts(possAnswers as List<of String>) returns List<of WordCount>
  return possAnswers.map(lambda w as String => new WordCount(w, maxWordCountRemainingAfterAttempt(possAnswers, w)))
end function

function betterOf(wc1 as WordCount, wc2 as WordCount, possAnswers as List<of String>) returns WordCount
  let isBetter be wc2.count < wc1.count
  let isEqualAndPossAnswer be (wc2.count is wc1.count) and possAnswers.contains(wc2.word)
  return if(isBetter or isEqualAndPossAnswer, wc2, wc1)
end function

function bestAttempt(possAnswers as List<of String>) returns String
  let wordCounts be allRemainingWordCounts(possAnswers)
  let best be wordCounts.reduce(wordCounts.head(), lambda bestSoFar as WordCount, newWord as WordCount => betterOf(bestSoFar, newWord, possAnswers))
  return best.word
end function

class WordCount
  constructor(word as String, count as Int)
    set this.word to word
    set this.count to count
  end constructor

  property word as String

  property count as Int

  function toString() returns String
    return $"{this.word} {this.count}"
  end function

end class

# 2309 words
constant allValidAnswers set to "ABACK ABASE ABATE ABBEY ABBOT ABHOR ABIDE ABLED ABODE ABORT ABOUT ABOVE ABUSE ABYSS ACORN ACRID ACTOR ACUTE ADAGE ADAPT ADEPT ADMIN ADMIT ADOBE ADOPT ADORE ADORN ADULT AFFIX AFIRE AFOOT AFOUL AFTER AGAIN AGAPE AGATE AGENT AGILE AGING AGLOW AGONY AGREE AHEAD AIDER AISLE ALARM ALBUM ALERT ALGAE ALIBI ALIEN ALIGN ALIKE ALIVE ALLAY ALLEY ALLOT ALLOW ALLOY ALOFT ALONE ALONG ALOOF ALOUD ALPHA ALTAR ALTER AMASS AMAZE AMBER AMBLE AMEND AMISS AMITY AMONG AMPLE AMPLY AMUSE ANGEL ANGER ANGLE ANGRY ANGST ANIME ANKLE ANNEX ANNOY ANNUL ANODE ANTIC ANVIL AORTA APART APHID APING APNEA APPLE APPLY APRON APTLY ARBOR ARDOR ARENA ARGUE ARISE ARMOR AROMA AROSE ARRAY ARROW ARSON ARTSY ASCOT ASHEN ASIDE ASKEW ASSAY ASSET ATOLL ATONE ATTIC AUDIO AUDIT AUGUR AUNTY AVAIL AVERT AVIAN AVOID AWAIT AWAKE AWARD AWARE AWASH AWFUL AWOKE AXIAL AXIOM AXION AZURE BACON BADGE BADLY BAGEL BAGGY BAKER BALER BALMY BANAL BANJO BARGE BARON BASAL BASIC BASIL BASIN BASIS BASTE BATCH BATHE BATON BATTY BAWDY BAYOU BEACH BEADY BEARD BEAST BEECH BEEFY BEFIT BEGAN BEGAT BEGET BEGIN BEGUN BEING BELCH BELIE BELLE BELLY BELOW BENCH BERET BERRY BERTH BESET BETEL BEVEL BEZEL BIBLE BICEP BIDDY BIGOT BILGE BILLY BINGE BINGO BIOME BIRCH BIRTH BISON BITTY BLACK BLADE BLAME BLAND BLANK BLARE BLAST BLAZE BLEAK BLEAT BLEED BLEEP BLEND BLESS BLIMP BLIND BLINK BLISS BLITZ BLOAT BLOCK BLOKE BLOND BLOOD BLOOM BLOWN BLUER BLUFF BLUNT BLURB BLURT BLUSH BOARD BOAST BOBBY BONEY BONGO BONUS BOOBY BOOST BOOTH BOOTY BOOZE BOOZY BORAX BORNE BOSOM BOSSY BOTCH BOUGH BOULE BOUND BOWEL BOXER BRACE BRAID BRAIN BRAKE BRAND BRASH BRASS BRAVE BRAVO BRAWL BRAWN BREAD BREAK BREED BRIAR BRIBE BRICK BRIDE BRIEF BRINE BRING BRINK BRINY BRISK BROAD BROIL BROKE BROOD BROOK BROOM BROTH BROWN BRUNT BRUSH BRUTE BUDDY BUDGE BUGGY BUGLE BUILD BUILT BULGE BULKY BULLY BUNCH BUNNY BURLY BURNT BURST BUSED BUSHY BUTCH BUTTE BUXOM BUYER BYLAW CABAL CABBY CABIN CABLE CACAO CACHE CACTI CADDY CADET CAGEY CAIRN CAMEL CAMEO CANAL CANDY CANNY CANOE CANON CAPER CAPUT CARAT CARGO CAROL CARRY CARVE CASTE CATCH CATER CATTY CAULK CAUSE CAVIL CEASE CEDAR CELLO CHAFE CHAFF CHAIN CHAIR CHALK CHAMP CHANT CHAOS CHARD CHARM CHART CHASE CHASM CHEAP CHEAT CHECK CHEEK CHEER CHESS CHEST CHICK CHIDE CHIEF CHILD CHILI CHILL CHIME CHINA CHIRP CHOCK CHOIR CHOKE CHORD CHORE CHOSE CHUCK CHUMP CHUNK CHURN CHUTE CIDER CIGAR CINCH CIRCA CIVIC CIVIL CLACK CLAIM CLAMP CLANG CLANK CLASH CLASP CLASS CLEAN CLEAR CLEAT CLEFT CLERK CLICK CLIFF CLIMB CLING CLINK CLOAK CLOCK CLONE CLOSE CLOTH CLOUD CLOUT CLOVE CLOWN CLUCK CLUED CLUMP CLUNG COACH COAST COBRA COCOA COLON COLOR COMET COMFY COMIC COMMA CONCH CONDO CONIC COPSE CORAL CORER CORNY COUCH COUGH COULD COUNT COUPE COURT COVEN COVER COVET COVEY COWER COYLY CRACK CRAFT CRAMP CRANE CRANK CRASH CRASS CRATE CRAVE CRAWL CRAZE CRAZY CREAK CREAM CREDO CREED CREEK CREEP CREME CREPE CREPT CRESS CREST CRICK CRIED CRIER CRIME CRIMP CRISP CROAK CROCK CRONE CRONY CROOK CROSS CROUP CROWD CROWN CRUDE CRUEL CRUMB CRUMP CRUSH CRUST CRYPT CUBIC CUMIN CURIO CURLY CURRY CURSE CURVE CURVY CUTIE CYBER CYCLE CYNIC DADDY DAILY DAIRY DAISY DALLY DANCE DANDY DATUM DAUNT DEALT DEATH DEBAR DEBIT DEBUG DEBUT DECAL DECAY DECOR DECOY DECRY DEFER DEIGN DEITY DELAY DELTA DELVE DEMON DEMUR DENIM DENSE DEPOT DEPTH DERBY DETER DETOX DEUCE DEVIL DIARY DICEY DIGIT DILLY DIMLY DINER DINGO DINGY DIODE DIRGE DIRTY DISCO DITCH DITTO DITTY DIVER DIZZY DODGE DODGY DOGMA DOING DOLLY DONOR DONUT DOPEY DOUBT DOUGH DOWDY DOWEL DOWNY DOWRY DOZEN DRAFT DRAIN DRAKE DRAMA DRANK DRAPE DRAWL DRAWN DREAD DREAM DRESS DRIED DRIER DRIFT DRILL DRINK DRIVE DROIT DROLL DRONE DROOL DROOP DROSS DROVE DROWN DRUID DRUNK DRYER DRYLY DUCHY DULLY DUMMY DUMPY DUNCE DUSKY DUSTY DUTCH DUVET DWARF DWELL DWELT DYING EAGER EAGLE EARLY EARTH EASEL EATEN EATER EBONY ECLAT EDICT EDIFY EERIE EGRET EIGHT EJECT EKING ELATE ELBOW ELDER ELECT ELEGY ELFIN ELIDE ELITE ELOPE ELUDE EMAIL EMBED EMBER EMCEE EMPTY ENACT ENDOW ENEMA ENEMY ENJOY ENNUI ENSUE ENTER ENTRY ENVOY EPOCH EPOXY EQUAL EQUIP ERASE ERECT ERODE ERROR ERUPT ESSAY ESTER ETHER ETHIC ETHOS ETUDE EVADE EVENT EVERY EVICT EVOKE EXACT EXALT EXCEL EXERT EXILE EXIST EXPEL EXTOL EXTRA EXULT EYING FABLE FACET FAINT FAIRY FAITH FALSE FANCY FANNY FARCE FATAL FATTY FAULT FAUNA FAVOR FEAST FECAL FEIGN FELLA FELON FEMME FEMUR FENCE FERAL FERRY FETAL FETCH FETID FETUS FEVER FEWER FIBER FICUS FIELD FIEND FIERY FIFTH FIFTY FIGHT FILER FILET FILLY FILMY FILTH FINAL FINCH FINER FIRST FISHY FIXER FIZZY FJORD FLACK FLAIL FLAIR FLAKE FLAKY FLAME FLANK FLARE FLASH FLASK FLECK FLEET FLESH FLICK FLIER FLING FLINT FLIRT FLOAT FLOCK FLOOD FLOOR FLORA FLOSS FLOUR FLOUT FLOWN FLUFF FLUID FLUKE FLUME FLUNG FLUNK FLUSH FLUTE FLYER FOAMY FOCAL FOCUS FOGGY FOIST FOLIO FOLLY FORAY FORCE FORGE FORGO FORTE FORTH FORTY FORUM FOUND FOYER FRAIL FRAME FRANK FRAUD FREAK FREED FREER FRESH FRIAR FRIED FRILL FRISK FRITZ FROCK FROND FRONT FROST FROTH FROWN FROZE FRUIT FUDGE FUGUE FULLY FUNGI FUNKY FUNNY FUROR FURRY FUSSY FUZZY GAFFE GAILY GAMER GAMMA GAMUT GASSY GAUDY GAUGE GAUNT GAUZE GAVEL GAWKY GAYER GAYLY GAZER GECKO GEEKY GEESE GENIE GENRE GHOST GHOUL GIANT GIDDY GIPSY GIRLY GIRTH GIVEN GIVER GLADE GLAND GLARE GLASS GLAZE GLEAM GLEAN GLIDE GLINT GLOAT GLOBE GLOOM GLORY GLOSS GLOVE GLYPH GNASH GNOME GODLY GOING GOLEM GOLLY GONAD GONER GOODY GOOEY GOOFY GOOSE GORGE GOUGE GOURD GRACE GRADE GRAFT GRAIL GRAIN GRAND GRANT GRAPE GRAPH GRASP GRASS GRATE GRAVE GRAVY GRAZE GREAT GREED GREEN GREET GRIEF GRILL GRIME GRIMY GRIND GRIPE GROAN GROIN GROOM GROPE GROSS GROUP GROUT GROVE GROWL GROWN GRUEL GRUFF GRUNT GUARD GUAVA GUESS GUEST GUIDE GUILD GUILE GUILT GUISE GULCH GULLY GUMBO GUMMY GUPPY GUSTO GUSTY GYPSY HABIT HAIRY HALVE HANDY HAPPY HARDY HAREM HARPY HARRY HARSH HASTE HASTY HATCH HATER HAUNT HAUTE HAVEN HAVOC HAZEL HEADY HEARD HEART HEATH HEAVE HEAVY HEDGE HEFTY HEIST HELIX HELLO HENCE HERON HILLY HINGE HIPPO HIPPY HITCH HOARD HOBBY HOIST HOLLY HOMER HONEY HONOR HORDE HORNY HORSE HOTEL HOTLY HOUND HOUSE HOVEL HOVER HOWDY HUMAN HUMID HUMOR HUMPH HUMUS HUNCH HUNKY HURRY HUSKY HUSSY HUTCH HYDRO HYENA HYMEN HYPER ICILY ICING IDEAL IDIOM IDIOT IDLER IDYLL IGLOO ILIAC IMAGE IMBUE IMPEL IMPLY INANE INBOX INCUR INDEX INEPT INERT INFER INGOT INLAY INLET INNER INPUT INTER INTRO IONIC IRATE IRONY ISLET ISSUE ITCHY IVORY JAUNT JAZZY JELLY JERKY JETTY JEWEL JIFFY JOINT JOIST JOKER JOLLY JOUST JUDGE JUICE JUICY JUMBO JUMPY JUNTA JUNTO JUROR KAPPA KARMA KAYAK KEBAB KHAKI KINKY KIOSK KITTY KNACK KNAVE KNEAD KNEED KNEEL KNELT KNIFE KNOCK KNOLL KNOWN KOALA KRILL LABEL LABOR LADEN LADLE LAGER LANCE LANKY LAPEL LAPSE LARGE LARVA LASSO LATCH LATER LATHE LATTE LAUGH LAYER LEACH LEAFY LEAKY LEANT LEAPT LEARN LEASE LEASH LEAST LEAVE LEDGE LEECH LEERY LEFTY LEGAL LEGGY LEMON LEMUR LEPER LEVEL LEVER LIBEL LIEGE LIGHT LIKEN LILAC LIMBO LIMIT LINEN LINER LINGO LIPID LITHE LIVER LIVID LLAMA LOAMY LOATH LOBBY LOCAL LOCUS LODGE LOFTY LOGIC LOGIN LOOPY LOOSE LORRY LOSER LOUSE LOUSY LOVER LOWER LOWLY LOYAL LUCID LUCKY LUMEN LUMPY LUNAR LUNCH LUNGE LUPUS LURCH LURID LUSTY LYING LYMPH LYRIC MACAW MACHO MACRO MADAM MADLY MAFIA MAGIC MAGMA MAIZE MAJOR MAKER MAMBO MAMMA MAMMY MANGA MANGE MANGO MANGY MANIA MANIC MANLY MANOR MAPLE MARCH MARRY MARSH MASON MASSE MATCH MATEY MAUVE MAXIM MAYBE MAYOR MEALY MEANT MEATY MECCA MEDAL MEDIA MEDIC MELEE MELON MERCY MERGE MERIT MERRY METAL METER METRO MICRO MIDGE MIDST MIGHT MILKY MIMIC MINCE MINER MINIM MINOR MINTY MINUS MIRTH MISER MISSY MOCHA MODAL MODEL MODEM MOGUL MOIST MOLAR MOLDY MONEY MONTH MOODY MOOSE MORAL MORON MORPH MOSSY MOTEL MOTIF MOTOR MOTTO MOULT MOUND MOUNT MOURN MOUSE MOUTH MOVER MOVIE MOWER MUCKY MUCUS MUDDY MULCH MUMMY MUNCH MURAL MURKY MUSHY MUSIC MUSKY MUSTY MYRRH NADIR NAIVE NANNY NASAL NASTY NATAL NAVAL NAVEL NEEDY NEIGH NERDY NERVE NEVER NEWER NEWLY NICER NICHE NIECE NIGHT NINJA NINNY NINTH NOBLE NOBLY NOISE NOISY NOMAD NOOSE NORTH NOSEY NOTCH NOVEL NUDGE NURSE NUTTY NYLON NYMPH OAKEN OBESE OCCUR OCEAN OCTAL OCTET ODDER ODDLY OFFAL OFFER OFTEN OLDEN OLDER OLIVE OMBRE OMEGA ONION ONSET OPERA OPINE OPIUM OPTIC ORBIT ORDER ORGAN OTHER OTTER OUGHT OUNCE OUTDO OUTER OUTGO OVARY OVATE OVERT OVINE OVOID OWING OWNER OXIDE OZONE PADDY PAGAN PAINT PALER PALSY PANEL PANIC PANSY PAPAL PAPER PARER PARKA PARRY PARSE PARTY PASTA PASTE PASTY PATCH PATIO PATSY PATTY PAUSE PAYEE PAYER PEACE PEACH PEARL PECAN PEDAL PENAL PENCE PENNE PENNY PERCH PERIL PERKY PESKY PESTO PETAL PETTY PHASE PHONE PHONY PHOTO PIANO PICKY PIECE PIETY PIGGY PILOT PINCH PINEY PINKY PINTO PIPER PIQUE PITCH PITHY PIVOT PIXEL PIXIE PIZZA PLACE PLAID PLAIN PLAIT PLANE PLANK PLANT PLATE PLAZA PLEAD PLEAT PLIED PLIER PLUCK PLUMB PLUME PLUMP PLUNK PLUSH POESY POINT POISE POKER POLAR POLKA POLYP POOCH POPPY PORCH POSER POSIT POSSE POUCH POUND POUTY POWER PRANK PRAWN PREEN PRESS PRICE PRICK PRIDE PRIED PRIME PRIMO PRINT PRIOR PRISM PRIVY PRIZE PROBE PRONE PRONG PROOF PROSE PROUD PROVE PROWL PROXY PRUDE PRUNE PSALM PUBIC PUDGY PUFFY PULPY PULSE PUNCH PUPIL PUPPY PUREE PURER PURGE PURSE PUSHY PUTTY PYGMY QUACK QUAIL QUAKE QUALM QUARK QUART QUASH QUASI QUEEN QUEER QUELL QUERY QUEST QUEUE QUICK QUIET QUILL QUILT QUIRK QUITE QUOTA QUOTE QUOTH RABBI RABID RACER RADAR RADII RADIO RAINY RAISE RAJAH RALLY RALPH RAMEN RANCH RANDY RANGE RAPID RARER RASPY RATIO RATTY RAVEN RAYON RAZOR REACH REACT READY REALM REARM REBAR REBEL REBUS REBUT RECAP RECUR RECUT REEDY REFER REFIT REGAL REHAB REIGN RELAX RELAY RELIC REMIT RENAL RENEW REPAY REPEL REPLY RERUN RESET RESIN RETCH RETRO RETRY REUSE REVEL REVUE RHINO RHYME RIDER RIDGE RIFLE RIGHT RIGID RIGOR RINSE RIPEN RIPER RISEN RISER RISKY RIVAL RIVER RIVET ROACH ROAST ROBIN ROBOT ROCKY RODEO ROGER ROGUE ROOMY ROOST ROTOR ROUGE ROUGH ROUND ROUSE ROUTE ROVER ROWDY ROWER ROYAL RUDDY RUDER RUGBY RULER RUMBA RUMOR RUPEE RURAL RUSTY SADLY SAFER SAINT SALAD SALLY SALON SALSA SALTY SALVE SALVO SANDY SANER SAPPY SASSY SATIN SATYR SAUCE SAUCY SAUNA SAUTE SAVOR SAVOY SAVVY SCALD SCALE SCALP SCALY SCAMP SCANT SCARE SCARF SCARY SCENE SCENT SCION SCOFF SCOLD SCONE SCOOP SCOPE SCORE SCORN SCOUR SCOUT SCOWL SCRAM SCRAP SCREE SCREW SCRUB SCRUM SCUBA SEDAN SEEDY SEGUE SEIZE SEMEN SENSE SEPIA SERIF SERUM SERVE SETUP SEVEN SEVER SEWER SHACK SHADE SHADY SHAFT SHAKE SHAKY SHALE SHALL SHALT SHAME SHANK SHAPE SHARD SHARE SHARK SHARP SHAVE SHAWL SHEAR SHEEN SHEEP SHEER SHEET SHEIK SHELF SHELL SHIED SHIFT SHINE SHINY SHIRE SHIRK SHIRT SHOAL SHOCK SHONE SHOOK SHOOT SHORE SHORN SHORT SHOUT SHOVE SHOWN SHOWY SHREW SHRUB SHRUG SHUCK SHUNT SHUSH SHYLY SIEGE SIEVE SIGHT SIGMA SILKY SILLY SINCE SINEW SINGE SIREN SISSY SIXTH SIXTY SKATE SKIER SKIFF SKILL SKIMP SKIRT SKULK SKULL SKUNK SLACK SLAIN SLANG SLANT SLASH SLATE SLEEK SLEEP SLEET SLEPT SLICE SLICK SLIDE SLIME SLIMY SLING SLINK SLOOP SLOPE SLOSH SLOTH SLUMP SLUNG SLUNK SLURP SLUSH SLYLY SMACK SMALL SMART SMASH SMEAR SMELL SMELT SMILE SMIRK SMITE SMITH SMOCK SMOKE SMOKY SMOTE SNACK SNAIL SNAKE SNAKY SNARE SNARL SNEAK SNEER SNIDE SNIFF SNIPE SNOOP SNORE SNORT SNOUT SNOWY SNUCK SNUFF SOAPY SOBER SOGGY SOLAR SOLID SOLVE SONAR SONIC SOOTH SOOTY SORRY SOUND SOUTH SOWER SPACE SPADE SPANK SPARE SPARK SPASM SPAWN SPEAK SPEAR SPECK SPEED SPELL SPELT SPEND SPENT SPERM SPICE SPICY SPIED SPIEL SPIKE SPIKY SPILL SPILT SPINE SPINY SPIRE SPITE SPLAT SPLIT SPOIL SPOKE SPOOF SPOOK SPOOL SPOON SPORE SPORT SPOUT SPRAY SPREE SPRIG SPUNK SPURN SPURT SQUAD SQUAT SQUIB STACK STAFF STAGE STAID STAIN STAIR STAKE STALE STALK STALL STAMP STAND STANK STARE STARK START STASH STATE STAVE STEAD STEAK STEAL STEAM STEED STEEL STEEP STEER STEIN STERN STICK STIFF STILL STILT STING STINK STINT STOCK STOIC STOKE STOLE STOMP STONE STONY STOOD STOOL STOOP STORE STORK STORM STORY STOUT STOVE STRAP STRAW STRAY STRIP STRUT STUCK STUDY STUFF STUMP STUNG STUNK STUNT STYLE SUAVE SUGAR SUING SUITE SULKY SULLY SUMAC SUNNY SUPER SURER SURGE SURLY SUSHI SWAMI SWAMP SWARM SWASH SWATH SWEAR SWEAT SWEEP SWEET SWELL SWEPT SWIFT SWILL SWINE SWING SWIRL SWISH SWOON SWOOP SWORD SWORE SWORN SWUNG SYNOD SYRUP TABBY TABLE TABOO TACIT TACKY TAFFY TAINT TAKEN TAKER TALLY TALON TAMER TANGO TANGY TAPER TAPIR TARDY TAROT TASTE TASTY TATTY TAUNT TAWNY TEACH TEARY TEASE TEDDY TEETH TEMPO TENET TENOR TENSE TENTH TEPEE TEPID TERRA TERSE TESTY THANK THEFT THEIR THEME THERE THESE THETA THICK THIEF THIGH THING THINK THIRD THONG THORN THOSE THREE THREW THROB THROW THRUM THUMB THUMP THYME TIARA TIBIA TIDAL TIGER TIGHT TILDE TIMER TIMID TIPSY TITAN TITHE TITLE TOAST TODAY TODDY TOKEN TONAL TONGA TONIC TOOTH TOPAZ TOPIC TORCH TORSO TORUS TOTAL TOTEM TOUCH TOUGH TOWEL TOWER TOXIC TOXIN TRACE TRACK TRACT TRADE TRAIL TRAIN TRAIT TRAMP TRASH TRAWL TREAD TREAT TREND TRIAD TRIAL TRIBE TRICE TRICK TRIED TRIPE TRITE TROLL TROOP TROPE TROUT TROVE TRUCE TRUCK TRUER TRULY TRUMP TRUNK TRUSS TRUST TRUTH TRYST TUBAL TUBER TULIP TULLE TUMOR TUNIC TURBO TUTOR TWANG TWEAK TWEED TWEET TWICE TWINE TWIRL TWIST TWIXT TYING UDDER ULCER ULTRA UMBRA UNCLE UNCUT UNDER UNDID UNDUE UNFED UNFIT UNIFY UNION UNITE UNITY UNLIT UNMET UNSET UNTIE UNTIL UNWED UNZIP UPPER UPSET URBAN URINE USAGE USHER USING USUAL USURP UTILE UTTER VAGUE VALET VALID VALOR VALUE VALVE VAPID VAPOR VAULT VAUNT VEGAN VENOM VENUE VERGE VERSE VERSO VERVE VICAR VIDEO VIGIL VIGOR VILLA VINYL VIOLA VIPER VIRAL VIRUS VISIT VISOR VISTA VITAL VIVID VIXEN VOCAL VODKA VOGUE VOICE VOILA VOMIT VOTER VOUCH VOWEL VYING WACKY WAFER WAGER WAGON WAIST WAIVE WALTZ WARTY WASTE WATCH WATER WAVER WAXEN WEARY WEAVE WEDGE WEEDY WEIGH WEIRD WELCH WELSH WHACK WHALE WHARF WHEAT WHEEL WHELP WHERE WHICH WHIFF WHILE WHINE WHINY WHIRL WHISK WHITE WHOLE WHOOP WHOSE WIDEN WIDER WIDOW WIDTH WIELD WIGHT WILLY WIMPY WINCE WINCH WINDY WISER WISPY WITCH WITTY WOKEN WOMAN WOMEN WOODY WOOER WOOLY WOOZY WORDY WORLD WORRY WORSE WORST WORTH WOULD WOUND WOVEN WRACK WRATH WREAK WRECK WREST WRING WRIST WRITE WRONG WROTE WRUNG WRYLY YACHT YEARN YEAST YIELD YOUNG YOUTH ZEBRA ZESTY ZONAL"

test test_betterOf
  let possAnswers be new List<of String>()
  let b2 be new WordCount("B", 2)
  let a3 be new WordCount("A", 3)
  let a2 be new WordCount("A", 2)
  assert betterOf(a3, b2, possAnswers) is b2
  assert betterOf(b2, a3, possAnswers) is b2
  assert betterOf(b2, a2, possAnswers) is b2
  assert betterOf(a2, b2, possAnswers) is a2
  let possAnswers2 be ["B"]
  assert betterOf(a2, b2, possAnswers2) is b2
  let possAnswers3 be ["B", "A"]
  assert betterOf(a2, b2, possAnswers3) is b2
  assert betterOf(b2, a2, possAnswers3) is a2
end test

test test_bestAttempt
  let possAnswers be ["ABCDE", "ABBBB", "EDCBA"]
  assert bestAttempt(possAnswers) is "EDCBA"
  let possAnswers2 be ["ABCDE", "ABBBB", "BCDEA"]
  assert bestAttempt(possAnswers2) is "BCDEA"
end test

test test_Wordcount
  let wc be new WordCount("ABCDE", 3)
  assert wc.word is "ABCDE"
  assert wc.count is 3
  assert wc.toString() is "ABCDE 3"
end test

test test_isGreen
  assert isGreen("ABCDE", "A____", 0) is true
  assert isGreen("ABCDE", "____E", 4) is true
  assert isGreen("ABCDE", "_A___", 1) is false
  assert isGreen("BABBB", "B____", 1) is false
end test

test test_setChar
  assert setChar("ABCDE", 0, "_") is "_BCDE"
  assert setChar("ABCDE", 4, "_") is "ABCD_"
end test

test test_setAttemptIfGreen
  assert setAttemptIfGreen("ABCDE", "ABCDE", 0) is "*BCDE"
  assert setAttemptIfGreen("ABCDE", "ABCDE", 4) is "ABCD*"
  assert setAttemptIfGreen("BBCDE", "ABCDE", 0) is "BBCDE"
  assert setAttemptIfGreen("ABCDE", "AACDE", 0) is "*BCDE"
end test

test test_setTargetIfGreen
  assert setTargetIfGreen("ABCDE", "ABCDE", 0) is ".BCDE"
  assert setTargetIfGreen("ABCDE", "ABCDE", 4) is "ABCD."
  assert setTargetIfGreen("BBCDE", "ABCDE", 0) is "ABCDE"
  assert setTargetIfGreen("ABCDE", "AACDE", 0) is ".ACDE"
end test

test test_evaluateGreens
  assert evaluateGreens("ABCDE", "AXXXX") is ["*BCDE", ".XXXX"]
  assert evaluateGreens("ABCDE", "XXXXE") is ["ABCD*", "XXXX."]
  assert evaluateGreens("ABCDE", "ABCDE") is ["*****", "....."]
  assert evaluateGreens("AACDE", "AXXXX") is ["*ACDE", ".XXXX"]
  assert evaluateGreens("ABCDE", "AAXXX") is ["*BCDE", ".AXXX"]
end test

test test_isYellow
  assert isYellow("ABCDE", "____A", 0) is true
  assert isYellow("ABCDE", "____A", 4) is false
  assert isYellow("ABCDE", "___AA", 0) is true
  assert isYellow("AACDE", "_A___", 1) is true
  assert isYellow("AACDE", "__A__", 1) is true
end test

test test_setAttemptIfYellow
  assert setAttemptIfYellow("ABCDE", "EABCD", 0) is "+BCDE"
  assert setAttemptIfYellow("ABCDE", "EABCD", 4) is "ABCD+"
  assert setAttemptIfYellow("ABCDE", "BAAAA", 0) is "+BCDE"
  assert setAttemptIfYellow("AAAAB", "EABBB", 4) is "AAAA+"
end test

test test_isAlreadyMarkedGreen
  assert isAlreadyMarkedGreen("AB*DE", 2) is true
  assert isAlreadyMarkedGreen("AB*DE", 0) is false
  assert isAlreadyMarkedGreen("AB*DE", 4) is false
  assert isAlreadyMarkedGreen("*BCD*", 2) is false
  assert isAlreadyMarkedGreen("*BCD*", 0) is true
  assert isAlreadyMarkedGreen("*BCD*", 4) is true
end test

test test_setTargetIfYellow
  assert setTargetIfYellow("ABCDE", "EABCD", 0) is "E.BCD"
  assert setTargetIfYellow("ABCDE", "EABCD", 4) is ".ABCD"
  assert setTargetIfYellow("ABCDE", "BAAAA", 0) is "B.AAA"
  assert setTargetIfYellow("AAAAB", "EABEA", 4) is "EA.EA"
  assert setTargetIfYellow("AAAAB", "EABBB", 4) is "EA.BB"
  assert setTargetIfYellow("*BCDE", "*BCDA", 4) is "*BCDA"
end test

test test_evaluateYellows
  assert evaluateYellows("ABCDE", "XAXXX") is ["+____", "X.XXX"]
  assert evaluateYellows("ABCDE", "XXXXA") is ["+____", "XXXX."]
  assert evaluateYellows("ABCDE", "XXXXE") is ["____+", "XXXX."]
  assert evaluateYellows("ABCDE", "XAAXX") is ["+____", "X.AXX"]
  assert evaluateYellows("AACDE", "XAXXX") is ["+____", "X.XXX"]
  assert evaluateYellows("ABCDE", "BCDEA") is ["+++++", "....."]
end test

test test_markAttempt
  assert markAttempt("ABCDE", "XXXXX") is "_____"
  assert markAttempt("ABCDE", "BCDEA") is "+++++"
  assert markAttempt("ABCDE", "ABCDE") is "*****"
  assert markAttempt("SAINT", "LADLE") is "_*___"
  assert markAttempt("IDEAL", "LADLE") is "_++++"
  assert markAttempt("CABAL", "RECAP") is "+__*_"
  assert markAttempt("CABAL", "RECAP") is "+__*_"
  assert markAttempt("COLON", "GLORY") is "_++__"
end test

test test_possibleAnswersAfterAttempt
  let prior be ["ABCDE", "BCDEA", "CDEAB", "DEABC", "EABCD"]
  assert possibleAnswersAfterAttempt(prior, "AAAAA", "*____") is ["ABCDE"]
  assert possibleAnswersAfterAttempt(prior, "AXXXX", "+____") is ["BCDEA", "CDEAB", "DEABC", "EABCD"]
  assert possibleAnswersAfterAttempt(prior, "AXXBX", "+__+_") is ["BCDEA", "CDEAB", "EABCD"]
end test

test test_maxWordCountRemainingAfterAttempt
  let prior be ["ABCDE", "BCDEA", "CDEAB", "DEABC", "EABCD"]
  let d be new Dictionary<of String, Int>()
  assert maxWordCountRemainingAfterAttempt(prior, "AAAAA") is 1
  assert maxWordCountRemainingAfterAttempt(prior, "AXXXX") is 4
  assert maxWordCountRemainingAfterAttempt(prior, "XXXXX") is 5
end test

test test_incrementCount
  let possAnswer be "BEGAN"
  let attempt be "BAGEL"
  let d be ["*+*_*":2, "*+*+_":3, "*+*++":0]
  let d2 be incrementCount(d, possAnswer, attempt)
  assert d2["*+*+_"] is 4
  assert d2["*+*_*"] is 2
  assert d2["*+*++"] is 0
end test

test test_allRemainingWordCounts
  let possAnswers be ["ABCDE", "BCDEA", "CDEAB", "DEABC", "EABCD"]
  let wordcounts be allRemainingWordCounts(possAnswers)
  assert wordcounts.toString() is "[ABCDE 4, BCDEA 4, CDEAB 4, DEABC 4, EABCD 4]"
end test
