Hi all,
Been wanting to practice and improve my Stata program writing skills. I thought Wordle presented a good opportunity try to write something. I think what I came up with basically works (but I may have missed edge cases), but it still feels pretty hacky to me (e.g. looping through all iterations and then dropping arrangements with repeated letters rather than doing some sort of sampling without replacement). Anyway - would love any advice or improvements you might have:
Been wanting to practice and improve my Stata program writing skills. I thought Wordle presented a good opportunity try to write something. I think what I came up with basically works (but I may have missed edge cases), but it still feels pretty hacky to me (e.g. looping through all iterations and then dropping arrangements with repeated letters rather than doing some sort of sampling without replacement). Anyway - would love any advice or improvements you might have:
Code:
capture program drop statle
program statle
clear all
set more off
*Clear out any commas
local no_commas = subinstr("`1'", ",", "", .)
local no_spaces = subinstr("`no_commas'", " ", "", .)
*Get the length of the string free of commas and spaces
local len = strlen("`no_spaces'")
*Remove commas and spaces from the position string as well
local pos_no_commas = subinstr("`2'", ",", "", .)
local pos_no_spaces = subinstr("`pos_no_commas'", " ", "", .)
*Use the length information to insert blanks if not already provided
if `len'==1{
local add_spaces = "`no_spaces'" + " _ _ _ _"
}
else if `len'==2{
local add_spaces = substr("`no_spaces'", 1, 1) + " " + ///
substr("`no_spaces'", 2, 1) + " _ _ _"
}
else if `len'==3{
local add_spaces = substr("`no_spaces'", 1, 1) + " " + ///
substr("`no_spaces'", 2, 1) + " " + ///
substr("`no_spaces'", 3, 1) + " _ _"
}
else if `len'==4{
local add_spaces = substr("`no_spaces'", 1, 1) + " " + ///
substr("`no_spaces'", 2, 1) + " " + ///
substr("`no_spaces'", 3, 1) + " " + ///
substr("`no_spaces'", 4, 1) + " _"
}
else if `len'==5{
local add_spaces = substr("`no_spaces'", 1, 1) + " " + ///
substr("`no_spaces'", 2, 1) + " " + ///
substr("`no_spaces'", 3, 1) + " " + ///
substr("`no_spaces'", 4, 1) + " " + ///
substr("`no_spaces'", 5, 1) +
}
*Throw some errors if you gave me no letters or too many letters
else if `len'==0{
di "ERROR: You specified `len' letters. You have to specify at least one."
stop
}
else{
di "ERROR: You specified `len' letters. That is too many."
stop
}
*Set a new dataset with one observation
set obs 1
*Initialize 5 blank variables that will store each letter by position
forvalues i = 1/5{
gen letter`i' = ""
}
*Start a counter to use in the loop below
local count = 1
*Loop through all possible combinations of the 5 letters, the dumb way
foreach letter1 of local add_spaces{
foreach letter2 of local add_spaces{
foreach letter3 of local add_spaces{
foreach letter4 of local add_spaces{
foreach letter5 of local add_spaces{
forvalues i = 1/5{
replace letter`i' = "`letter`i''" in `count'
local new_n = _N + 1
set obs `new_n'
}
local ++count
}
}
}
}
}
*Store a version of the string with no blanks
local no_blanks = subinstr("`add_spaces'", "_", "", .)
*Create a series of dummies to tell us if specific letter is in a specific position
foreach letter of local no_blanks{
forvalues i = 1/5{
gen letter`i'_is_`letter' = letter`i'=="`letter'"
}
*Calculate the number of times each letter appears
egen row_`letter'_count = rowtotal(*_is_`letter')
*Drop the combination if the letter appears more than once or not at all
drop if row_`letter'_count>1 & !missing(row_`letter'_count) | ///
row_`letter'_count==0
}
if "`pos_no_spaces'"=="_____"{
di "No Need to Check Positions!"
}
else{
/*Loop through all the letters to check the positions of the letter combinations
against the known positions
*/
foreach letter of local no_blanks{
*Initialize a blank variable for storing indicators of out of position letters
gen drop_because_of_`letter' = .
*Store the known position of the letter in the submitted string with known locations
local let_pos = strpos("`pos_no_spaces'", "`letter'")
*If we don't know the positon of a letter, we pass
if `let_pos'==0{
di "No position of `letter' determined"
}
/* If we do know the position of a letter, we check whether the letter is in the
right position by checking the letter of that number against the letter
it should be
*/
else{
replace drop_because_of_`letter' = letter`let_pos'!="`letter'"
}
}
*Aggregate across the drop indicators to make one single drop indicator
egen to_drop = rowmax(drop_*)
*Drop the combinations that don't meet the criteria
drop if to_drop
}
*Drop all the weird variables we made along the way
drop *_*
*Combine the letter options into one string
gen arrangement = letter1+letter2+letter3+letter4+letter5
*Tab all the remaining arrangements
tab arrangement
end

Comment