release winning entries of IOCCC28 under the year 2024
Congratulations to: * 2024/anders - Prize in sound coding - layered music generator * 2024/burton - Prize in pentagrammatology - wordle hard mode solver * 2024/cable1 - Prize in bot talk - LLM inference engine * 2024/cable2 - Prize in murky waters - salmon recipe * 2024/carlini - Prize in perfect timing - Intel 4004 emulation * 2024/codemeow - Prize in tray planting - bonsai generator * 2024/endoh1 - Prize in patient pointillism - slow ray tracer in CPP * 2024/endoh2 - Prize in solid body physics - rotating rigid body simulation * 2024/ferguson1 - Prize in diabolical logistics - Oregon trail pact with the devil * 2024/ferguson2 - Prize in yil-tas - Navajo code talker translator * 2024/howe - Nice accent eh-ward - mini vi * 2024/kramer - Prize in linguistic arithmetic - natural language numerical calculator * 2024/kurdyukov1 - Prize in phased periodicity - phase of the Moon * 2024/kurdyukov2 - Prize in art restoration - JPEG artifact removal * 2024/kurdyukov3 - Prize in virtual quietus - gaming VM and doom * 2024/kurdyukov4 - Prize in embiggening - encode without literals * 2024/maffiodo - Prize in creative interpretation - Jav\*script interpreter * 2024/mills - Prize in ℤ₃ - Infocom v3 story interpreter * 2024/stedolan - Best one liner - icon from MD5 * 2024/straadt - Prize in imitative rebooting - OpenRISC CPU and Linux * 2024/tmarrec - Prize in cyclonic coding - 3D tornado simulation * 2024/tompng - Prize in quasi bijection - vortex encryptor decryptor * 2024/weaver - Sur prize - rickroll Ran `make update` to perform the above. new file: 2024/burton/.entry.json
This commit is contained in:
1 parent
10512e6c79
commit
09750f8899
443 files changed
+182938
-231
No files matched your search
@@ -0,0 +1,361 @@
|
||||
{
|
||||
"no_comment" : "mandatory comment: because comments were removed from the original JSON spec",
|
||||
"entry_JSON_format_version" : "1.2 2024-09-25",
|
||||
"award" : "Prize in pentagrammatology",
|
||||
"year" : 2024,
|
||||
"dir" : "burton",
|
||||
"entry_id" : "2024_burton",
|
||||
"title" : "wordle",
|
||||
"abstract" : "wordle hard mode solver",
|
||||
"author_set" : [
|
||||
{
|
||||
"author_handle" : "Dave_Burton"
|
||||
}
|
||||
],
|
||||
"manifest" : [
|
||||
{
|
||||
"file_path" : "prog.c",
|
||||
"inventory_order" : 20,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "c",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "entry source code"
|
||||
},
|
||||
{
|
||||
"file_path" : "Makefile",
|
||||
"inventory_order" : 30,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "makefile",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "entry Makefile"
|
||||
},
|
||||
{
|
||||
"file_path" : "prog.orig.c",
|
||||
"inventory_order" : 50,
|
||||
"OK_to_edit" : false,
|
||||
"display_as" : "c",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "original source code"
|
||||
},
|
||||
{
|
||||
"file_path" : "deobfuscated.html",
|
||||
"inventory_order" : 100,
|
||||
"OK_to_edit" : false,
|
||||
"display_as" : "markdown",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "about deobfuscation"
|
||||
},
|
||||
{
|
||||
"file_path" : "dict",
|
||||
"inventory_order" : 100,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "5-letter wordle dictionary"
|
||||
},
|
||||
{
|
||||
"file_path" : "extra.html",
|
||||
"inventory_order" : 100,
|
||||
"OK_to_edit" : false,
|
||||
"display_as" : "markdown",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "description of the extra files"
|
||||
},
|
||||
{
|
||||
"file_path" : "reveal.c",
|
||||
"inventory_order" : 100,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "c",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "reveal wordle hints"
|
||||
},
|
||||
{
|
||||
"file_path" : "try.sh",
|
||||
"inventory_order" : 100,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "script to try entry"
|
||||
},
|
||||
{
|
||||
"file_path" : "deobfuscated.c",
|
||||
"inventory_order" : 150,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "c",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "deobfuscated source code"
|
||||
},
|
||||
{
|
||||
"file_path" : "gen.c",
|
||||
"inventory_order" : 150,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "c",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "simulate wordle games"
|
||||
},
|
||||
{
|
||||
"file_path" : "worst",
|
||||
"inventory_order" : 150,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "determine worst target words"
|
||||
},
|
||||
{
|
||||
"file_path" : "2024_01_18_wordle_stats.png",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "image",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "wordle stats"
|
||||
},
|
||||
{
|
||||
"file_path" : "avg",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "simple analysis script"
|
||||
},
|
||||
{
|
||||
"file_path" : "comp.avg",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "compute averages"
|
||||
},
|
||||
{
|
||||
"file_path" : "dups.c",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "c",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "remove repeated letters"
|
||||
},
|
||||
{
|
||||
"file_path" : "exhaust",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "exhaustive test of all wordle words"
|
||||
},
|
||||
{
|
||||
"file_path" : "extra_guesses",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "allow extra wordle guesses"
|
||||
},
|
||||
{
|
||||
"file_path" : "freq",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "letter frequency in wordle words"
|
||||
},
|
||||
{
|
||||
"file_path" : "mkresults",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "report showing average word accuracy"
|
||||
},
|
||||
{
|
||||
"file_path" : "rank",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "how averages are calculated and missed words"
|
||||
},
|
||||
{
|
||||
"file_path" : "run.c",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "c",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "automated testing tool"
|
||||
},
|
||||
{
|
||||
"file_path" : "unob.c",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "c",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "unobfuscator tool source code"
|
||||
},
|
||||
{
|
||||
"file_path" : "wavg",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "weigh six scores"
|
||||
},
|
||||
{
|
||||
"file_path" : "word.result",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "produce results of a"
|
||||
},
|
||||
{
|
||||
"file_path" : "wrank",
|
||||
"inventory_order" : 200,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "shellscript",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "weighted rank tool"
|
||||
},
|
||||
{
|
||||
"file_path" : "2024_burton.tar.bz2",
|
||||
"inventory_order" : 1415926535,
|
||||
"OK_to_edit" : false,
|
||||
"display_as" : "tbz2",
|
||||
"display_via_github" : false,
|
||||
"entry_text" : "download entry tarball"
|
||||
},
|
||||
{
|
||||
"file_path" : ".entry.json",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "json",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "entry summary and manifest in JSON"
|
||||
},
|
||||
{
|
||||
"file_path" : ".gitignore",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "gitignore",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "list of files that should not be committed under git"
|
||||
},
|
||||
{
|
||||
"file_path" : ".path",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : false,
|
||||
"display_as" : "path",
|
||||
"display_via_github" : false,
|
||||
"entry_text" : "directory path from top level directory"
|
||||
},
|
||||
{
|
||||
"file_path" : "README.md",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "markdown",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "markdown source for this web page"
|
||||
},
|
||||
{
|
||||
"file_path" : "correct.test.abode",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "correct test of the word abode"
|
||||
},
|
||||
{
|
||||
"file_path" : "correct.test.jaunt",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "correct test of the word jaunt"
|
||||
},
|
||||
{
|
||||
"file_path" : "correct.test.light",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "correct test of the word light"
|
||||
},
|
||||
{
|
||||
"file_path" : "deobfuscated.md",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "markdown",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "markdown source for deobfuscated.html"
|
||||
},
|
||||
{
|
||||
"file_path" : "extra.md",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "markdown",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "markdown source for extra.html"
|
||||
},
|
||||
{
|
||||
"file_path" : "r.blast",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "analysis for the word blast"
|
||||
},
|
||||
{
|
||||
"file_path" : "r.crate",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "analysis for the word crate"
|
||||
},
|
||||
{
|
||||
"file_path" : "r.query",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "analysis for the word query"
|
||||
},
|
||||
{
|
||||
"file_path" : "r.terns",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "analysis for the word terns"
|
||||
},
|
||||
{
|
||||
"file_path" : "test.abode",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "analysis for the word"
|
||||
},
|
||||
{
|
||||
"file_path" : "test.jaunt",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "analysis for the jaunt"
|
||||
},
|
||||
{
|
||||
"file_path" : "test.light",
|
||||
"inventory_order" : 4000000000,
|
||||
"OK_to_edit" : true,
|
||||
"display_as" : "text",
|
||||
"display_via_github" : true,
|
||||
"entry_text" : "analysis for the word light"
|
||||
},
|
||||
{
|
||||
"file_path" : "index.html",
|
||||
"inventory_order" : 4294967295,
|
||||
"OK_to_edit" : false,
|
||||
"display_as" : "html",
|
||||
"display_via_github" : false,
|
||||
"entry_text" : "this web page"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
dups
|
||||
fmt
|
||||
fmt.c
|
||||
gen
|
||||
reveal
|
||||
run
|
||||
usdw
|
||||
@@ -0,0 +1 @@
|
||||
2024/burton
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env make
|
||||
#
|
||||
# 2024/burton
|
||||
|
||||
#############################
|
||||
# shell used by this Makefile
|
||||
#############################
|
||||
|
||||
SHELL= bash
|
||||
|
||||
|
||||
#######################
|
||||
# common tool locations
|
||||
#######################
|
||||
|
||||
include ../../var.mk
|
||||
|
||||
|
||||
#####################
|
||||
# C compiler settings
|
||||
#####################
|
||||
|
||||
# Common C compiler warnings to silence
|
||||
#
|
||||
# Example: CSILENCE= -Wno-poison-system-directories
|
||||
#
|
||||
CSILENCE= -Wno-misleading-indentation -Wno-parentheses -Wno-unused-value
|
||||
|
||||
# Attempt to silence unknown warning options
|
||||
#
|
||||
CUNKNOWN= -Wno-unknown-warning-option
|
||||
|
||||
# Common C compiler warning flags
|
||||
#
|
||||
# NOTE: The addition of -pedantic to CWARN is a challenge that
|
||||
# You may wish to avoid if it proves too problematic.
|
||||
# There is NO penalty for removing -pedantic from CWARN.
|
||||
#
|
||||
CWARN= -Wall -Wextra -pedantic ${CSILENCE} ${CUNKNOWN}
|
||||
|
||||
# Compiler standard
|
||||
#
|
||||
CSTD= -std=gnu17
|
||||
|
||||
# Compiler bit architecture
|
||||
#
|
||||
# Example for 32-bitness: ARCH= -m32
|
||||
# Example for 64-bitness: ARCH= -m64
|
||||
#
|
||||
# NOTE: Normally one should NOT specify a specific architecture.
|
||||
#
|
||||
ARCH=
|
||||
|
||||
# Defines that are needed to compile
|
||||
#
|
||||
# Example: -Dfoo -Dbar=baz
|
||||
#
|
||||
CDEFINE= -D_FORTIFY_SOURCE=0
|
||||
|
||||
# Include files that are needed to compile
|
||||
#
|
||||
# Example: CINCLUDE= -include stdio.h
|
||||
#
|
||||
CINCLUDE=
|
||||
|
||||
# Other flags to pass to the C compiler
|
||||
#
|
||||
# Example: COTHER= -fno-math-errno
|
||||
#
|
||||
COTHER=
|
||||
|
||||
# Optimization
|
||||
#
|
||||
# NOTE: Feel free to change the level of compiler optimization.
|
||||
# The "-O3" is just a friendly default you might wish to try.
|
||||
#
|
||||
# Example: OPT= -O0 -g
|
||||
#
|
||||
OPT= -O3
|
||||
|
||||
# Default flags for ANSI C compilation
|
||||
#
|
||||
CFLAGS= ${CSTD} ${CWARN} ${ARCH} ${CDEFINE} ${CINCLUDE} ${COTHER} ${OPT}
|
||||
|
||||
# Libraries needed to build
|
||||
#
|
||||
# Example: LDFLAGS= -lncurses -lm
|
||||
#
|
||||
LDFLAGS=
|
||||
|
||||
# C compiler to use
|
||||
#
|
||||
# NOTE: The IOCCC Judges recommend you leave CC as just "cc"
|
||||
# and NOT set it to clang, or gcc, or something else
|
||||
# unless you have a STRONG reason to do so.
|
||||
#
|
||||
# Setting CC to something other than "cc" makes your
|
||||
# code less portable to those who do not have your
|
||||
# particular C compiler. **hint**
|
||||
#
|
||||
# If you want to test your code with a particular C compiler,
|
||||
# use the make command line. For example:
|
||||
#
|
||||
# make all CC=clang
|
||||
# make all CC=gcc
|
||||
#
|
||||
CC= cc
|
||||
|
||||
# Compiler add-ons or replacements for clang only
|
||||
#
|
||||
ifeq "$(findstring $(CLANG),${CC})" "$(CLANG)"
|
||||
#
|
||||
# NOTE: This code is only invoked when CC is "clang"
|
||||
# such as when you use the make command line:
|
||||
#
|
||||
# make all CC=clang
|
||||
#
|
||||
CSILENCE+= -Wno-array-bounds-pointer-arithmetic -Wno-comma \
|
||||
-Wno-declaration-after-statement -Wno-format-nonliteral \
|
||||
-Wno-implicit-int-conversion -Wno-missing-prototypes \
|
||||
-Wno-missing-variable-declarations -Wno-poison-system-directories \
|
||||
-Wno-shadow -Wno-shorten-64-to-32 -Wno-vla -Wno-extra-semi-stmt \
|
||||
-Wno-misleading-indentation -Wno-unsafe-buffer-usage
|
||||
#
|
||||
CWARN+= -Weverything
|
||||
#
|
||||
endif
|
||||
|
||||
# Specific add-ons or replacements for gcc only
|
||||
#
|
||||
ifeq "$(findstring $(GCC),${CC})" "$(GCC)"
|
||||
#
|
||||
# NOTE: This code is only invoked when CC is "gcc"
|
||||
# such as when you use the make command line:
|
||||
#
|
||||
# make all CC=gcc
|
||||
#
|
||||
CSILENCE+=
|
||||
#
|
||||
CWARN+=
|
||||
#
|
||||
endif
|
||||
|
||||
|
||||
###########################################
|
||||
# Special Makefile variables for this entry
|
||||
###########################################
|
||||
|
||||
ENTRY= prog
|
||||
PROG= ${ENTRY}
|
||||
#
|
||||
OBJ= ${PROG}.o
|
||||
TARGET= ${PROG} ${TOOLS}
|
||||
#
|
||||
ALT_OBJ=
|
||||
ALT_TARGET=
|
||||
|
||||
# list any data files supplied with your submission
|
||||
#
|
||||
# Example: DATA= curds whey
|
||||
#
|
||||
DATA= dict test.abode test.light test.jaunt correct.test.abode \
|
||||
correct.test.jaunt correct.test.light r.blast r.crate r.query r.terns
|
||||
|
||||
# utility scripts
|
||||
#
|
||||
SCRIPTS= avg comp.avg exhaust extra_guesses freq mkresults rank wavg word.result worst wrank
|
||||
TOOLS= gen dups run reveal usdw
|
||||
IOCCCSIZE= iocccsize
|
||||
DICTIONARY= /usr/share/dict/words
|
||||
|
||||
|
||||
#################
|
||||
# build the entry
|
||||
#################
|
||||
|
||||
all: data ${TARGET} ${SCRIPTS} ${TOOLS}
|
||||
@${TRUE}
|
||||
|
||||
.PHONY: all alt data everything clean clobber
|
||||
|
||||
# how to compile
|
||||
#
|
||||
${PROG}: ${PROG}.c
|
||||
${CC} ${CFLAGS} ${PROG}.c -o $@ ${LDFLAGS}
|
||||
|
||||
define stest
|
||||
echo -n "$(2): ";
|
||||
if ${CAT} $(2) | $(1) dict | ${DIFF} - correct.$(2); then echo pass; else echo fail; fi
|
||||
endef
|
||||
|
||||
test: prog size
|
||||
@ $(call stest,./prog,test.light,-lt)
|
||||
@ $(call stest,./prog,test.abode,-lt)
|
||||
@ $(call stest,./prog,test.jaunt,-eq)
|
||||
|
||||
size:
|
||||
${IOCCCSIZE} prog.c
|
||||
@# iocccsize-2019 -i < prog.c
|
||||
@# iocccsize-2020 -i < prog.c > /dev/null
|
||||
|
||||
tfmt: fmt
|
||||
@ $(call stest,./fmt,test.light,-lt)
|
||||
@ $(call stest,./fmt,test.abode,-lt)
|
||||
@ $(call stest,./fmt,test.jaunt,-eq)
|
||||
|
||||
fmt: fmt.c
|
||||
${CC} ${CFLAGS} fmt.c -o $@ ${LDFLAGS}
|
||||
|
||||
fmt.c: prog.c
|
||||
${RM} -f $@
|
||||
${CLANG_FORMAT} -style=file < prog.c > fmt.c
|
||||
|
||||
dups: dups.c
|
||||
${CC} ${CFLAGS} dups.c -o $@ ${LDFLAGS}
|
||||
|
||||
gen: gen.c
|
||||
${CC} ${CFLAGS} gen.c -o $@ ${LDFLAGS}
|
||||
|
||||
run: run.c
|
||||
${CC} ${CFLAGS} run.c -o $@ ${LDFLAGS}
|
||||
|
||||
reveal: reveal.c
|
||||
${CC} ${CFLAGS} reveal.c -o $@ ${LDFLAGS}
|
||||
|
||||
usdw:
|
||||
${RM} -f $@
|
||||
${GREP} '^.....$$' ${DICTIONARY} | ${GREP} -v '[A-Z]' | ${GREP} -v beant > $@
|
||||
|
||||
spoiler: reveal fmt.c
|
||||
@if test -x ${CLANG_FORMAT}; then ${CLANG_FORMAT} -style=file < prog.c | ./reveal; else ./reveal < fmt.c; fi
|
||||
|
||||
# alternative executable
|
||||
#
|
||||
alt: data ${ALT_TARGET}
|
||||
@${TRUE}
|
||||
|
||||
${PROG}.alt: ${PROG}.alt.c
|
||||
${CC} ${CFLAGS} ${PROG}.alt.c -o $@ ${LDFLAGS}
|
||||
|
||||
# data files
|
||||
#
|
||||
data: ${DATA}
|
||||
@${TRUE}
|
||||
|
||||
# both all and alt
|
||||
#
|
||||
everything: all alt
|
||||
@${TRUE}
|
||||
|
||||
|
||||
###############
|
||||
# utility rules
|
||||
###############
|
||||
#
|
||||
clean:
|
||||
${RM} -f ${OBJ} ${ALT_OBJ}
|
||||
|
||||
clobber: clean
|
||||
${RM} -f ${TARGET} ${ALT_TARGET}
|
||||
${RM} -rf *.dSYM
|
||||
${RM} -f fmt.c fmt
|
||||
|
||||
|
||||
######################################
|
||||
# optional include of 1337 hacker rulz
|
||||
######################################
|
||||
|
||||
-include 1337.mk ../1337.mk ../../1337.mk
|
||||
@@ -0,0 +1,546 @@
|
||||
## To build:
|
||||
|
||||
``` <!---sh-->
|
||||
make all
|
||||
```
|
||||
|
||||
|
||||
## To use:
|
||||
|
||||
``` <!---sh-->
|
||||
./prog dict
|
||||
```
|
||||
|
||||
|
||||
## Try:
|
||||
|
||||
``` <!---sh-->
|
||||
./try.sh
|
||||
```
|
||||
|
||||
|
||||
## Judges' remarks:
|
||||
|
||||
We were impressed with the algorithm, but one question remains: is there a
|
||||
strategy which reduces the probability of a loss even further, at the cost of
|
||||
requiring a higher average number of guesses?
|
||||
|
||||
|
||||
## Author's remarks:
|
||||
|
||||
This program is an assistant for solving hard-mode [Wordle(tm)][1]
|
||||
(you must use any revealed hints in all subsequent guesses)
|
||||
accurately/efficiently - in the fewest guesses.
|
||||
The program expects lower case input in two words: `guess color`,
|
||||
followed by newline.
|
||||
|
||||
- `guess` is the 5 letter word you guessed,
|
||||
- `color` is 5 letters from the set [g,y,b]
|
||||
|
||||
that correspond to the colors shown in Wordle: g=green, y=yellow, b=black.
|
||||
|
||||
[1]: https://www.nytimes.com/games/wordle/index.html
|
||||
|
||||
Start the program with a single argument, the name of a dictionary of words.
|
||||
`/usr/share/dict/words` is a perfectly cromulent dictionary,
|
||||
but the included file `dict` contains the 2315 target words from the original
|
||||
Wordle game and consistently provides the fewest guesses.
|
||||
|
||||
As an example session, Wordle 226 has the target word `light`.
|
||||
The recommended first guess (for all Wordle puzzles) is `blast`, `terns`,
|
||||
or `crate` (see the [extra.html](extra.html) that accompanies this entry for details).
|
||||
|
||||
Enter `terns` in game 226 of Wordle, and click the enter button.
|
||||
The letters will turn yellow, black, black, black, black.
|
||||
This assistant prompts "? ", and the input expected is the guessed word
|
||||
followed by the first letters of the color revealed for that guess:
|
||||
|
||||
``` <!---sh-->
|
||||
$ prog dict
|
||||
? terns ybbbb
|
||||
loath
|
||||
? loath gbbyy
|
||||
light
|
||||
$
|
||||
```
|
||||
|
||||
The program responds with the recommended next guess. Enter
|
||||
this word `loath` into the game, which then reveals the colors
|
||||
green, black, black, yellow, yellow. Typing `loath gbbyy` into
|
||||
this assistant shows the solution is `light`, and exits.
|
||||
Type this word into the game, click enter, and viola!
|
||||
Solved in 3 guesses: terns > loath > light.
|
||||
|
||||
This program averages better than 4 guesses on the 1566 starting words
|
||||
without duplicate letters created by `./dups -n < dict`,
|
||||
ranging from 3.587 to 3.997, with an overall average of 3.748 guesses.
|
||||
|
||||
This is excellent performance, and close to optimal.
|
||||
See this YouTube presentation on [solving Wordle using information theory][2],
|
||||
which shows a hard mode average of 3.562 when using `crane`
|
||||
(time offset 29:58). There is a small correction video changing
|
||||
[`crane` to `salet`, `trace` or `crate`][3].
|
||||
NB: These videos are NOT spoilers for this algorithm.
|
||||
|
||||
[2]: https://www.youtube.com/watch?v=v68zYyaEmEA
|
||||
[3]: https://www.youtube.com/watch?v=fRed0Xmc2Wg
|
||||
|
||||
`prog` does NOT require you to use its offered guess.
|
||||
If you think you have a better guess than offered,
|
||||
simply type your guess and the Wordle/`gen` responses into `prog`;
|
||||
it will internally re-adjust using your input instead of its guess,
|
||||
and produce what it thinks is the best next guess after your input.
|
||||
|
||||
|
||||
### A Feature
|
||||
|
||||
Caution: When there are duplicate letters in the guess word,
|
||||
and one of them is marked black and the other non-black,
|
||||
this program MUST have the black letter marked *yellow*.
|
||||
The example session (Wordle 228) below demonstrates this:
|
||||
|
||||
```
|
||||
# do NOT do this
|
||||
$ prog dict
|
||||
? terns bbbby
|
||||
shoal
|
||||
? shoal gggbb
|
||||
shook
|
||||
? shook gggbg <<-- this is what Wordle shows
|
||||
xyzzy <<-- no candidate words match
|
||||
|
||||
# do THIS instead
|
||||
$ prog dict
|
||||
? terns bbbby
|
||||
shoal
|
||||
? shoal gggbb
|
||||
shook
|
||||
? shook gggyg <<-- not gggbg as shown in the game
|
||||
shock
|
||||
? shock ggggg
|
||||
shock
|
||||
```
|
||||
|
||||
In the guess word `shook`, there are 2 o's, but only one shown valid in
|
||||
the puzzle. If you mark the leftmost o as black (as according to the
|
||||
Wordle game), this program cannot find the winning word (`shock`),
|
||||
producing the error word `xyzzy`.
|
||||
|
||||
The included simulator `gen` produces the required transformation of b -> y.
|
||||
|
||||
The extra program `run` connects `gen` and `prog`, allowing for quick tests.
|
||||
|
||||
```
|
||||
make tools
|
||||
./run dict terns shock # test starting guess `terns` against target `shock`
|
||||
```
|
||||
|
||||
Using `run` allows one to quickly and easily experiment with different
|
||||
starting words for different target words, seeing how the guesses progress.
|
||||
|
||||
(A late tweak to the program improved its accuracy, and in the above example,
|
||||
the progression is now terns > shoal > shock. However, the caution remains
|
||||
for any duplicate letter guesses.)
|
||||
|
||||
|
||||
### Failure mode
|
||||
|
||||
The following 4 words from the included file `dict` are not guessed correctly
|
||||
within 6 guesses when using the recommended (via exhaustive testing)
|
||||
starting guess of `blast`. Since this program is deterministic, these
|
||||
four words always require 7 guesses when starting with `blast`, yielding
|
||||
a failure rate of 4/2315, or a success rate of 99.8%.
|
||||
|
||||
```
|
||||
foyer jaunt shave wound
|
||||
```
|
||||
|
||||
Using the ancillary programs `run`, `exhaust`, and `mkresults`,
|
||||
the starting word `blast` yields the fewest misses at these four words,
|
||||
with an accuracy/efficiency of 3.67. See [extra.html](extra.html)
|
||||
for more detail.
|
||||
|
||||
This program also has a benefit the game Wordle does not:
|
||||
Wordle allows several thousand extra words to use in guesses not in `dict`.
|
||||
This program does not restrict _any_ words, so one could enter any
|
||||
combination of 5 letters as input, except these are unlikely to be accepted
|
||||
by the Wordle game, and this program selects its words from the much more
|
||||
limited set that is present in `dict`. Using an expanded dictionary like
|
||||
`/usr/share/dict/words` provides a richer set of guesses, but generally
|
||||
increases the average number of guesses and the failure rate.
|
||||
|
||||
|
||||
### Interesting questions
|
||||
|
||||
- How are candidate words chosen?
|
||||
- How does the array of numbers relate to finding a word?
|
||||
- It is interesting to see the final stages of picking a word.
|
||||
You might be interested to add observability? (Hint: `make tfmt`)
|
||||
Although `clang-format` is unlikely to help you understand the code,
|
||||
`printf`'s will help you understand how the "tables" are used:
|
||||
|
||||
"Show me your flowcharts and conceal your tables,
|
||||
and I shall continue to by mystified;
|
||||
show me your tables and I won't usually need your flowcharts...."
|
||||
-- Fred Brooks
|
||||
|
||||
- There is a plausible "tunable", not exposed. It selects when to
|
||||
discard certain words. What is this value, and how important is this
|
||||
value in optimizing results?
|
||||
- There is a debatable "bug" in the code, but it actually _improves_ the
|
||||
results. Where and why?
|
||||
Hint: the limit `for` a loop. ;-)
|
||||
- This solver/assistant is pretty interesting to play with, experiment with.
|
||||
Can you improve it?
|
||||
|
||||
|
||||
### Did you notice?
|
||||
|
||||
- Wordle is spelled in the code several different ways, one is obscure.
|
||||
If you need help, try this command (which requires `clang-format`):
|
||||
|
||||
```
|
||||
make spoiler
|
||||
```
|
||||
|
||||
- My second favorite language is spelled out thrice in the source code.
|
||||
- Variables are mostly generic a,b,c and i,j,k,l and reused in different
|
||||
scopes. Have fun!
|
||||
- The Sierpinski[4] number? What in the world is a Sierpinski number?
|
||||
|
||||
[4]: https://planetmath.org/sierpinskinumber
|
||||
|
||||
|
||||
### Time Passages (Apologies to Al Stewart)
|
||||
|
||||
This code was written in mid January 2022, before the NYT purchased Wordle[tm].
|
||||
|
||||
I have since used this assistant to play the game for more than a year,
|
||||
confirming my local experiments.
|
||||
|
||||

|
||||
|
||||
The dictionary changed in 2023! In late March 2023, I encountered the first
|
||||
word not in `dict`: `guano`. In a bit of a surprise, I tried:
|
||||
|
||||
```
|
||||
blast bbgbb
|
||||
reach bbgbb
|
||||
piano bbggg
|
||||
xyzzy
|
||||
```
|
||||
|
||||
What? The only word in `dict` ending in `ano` is `piano`. So I tried again
|
||||
using `/usr/share/dict/words`:
|
||||
|
||||
```
|
||||
blast bbgbb
|
||||
crane bbggb
|
||||
guano ggggg
|
||||
```
|
||||
|
||||
Blending the two, the puzzle was guessed in 4 tries total:
|
||||
|
||||
```
|
||||
blast > reach > piano > guano
|
||||
```
|
||||
|
||||
So caveat emptor: while `dict` used to be authoritative, we know some words
|
||||
were deemed offensive and removed. More problematic, some words were added,
|
||||
although I am presently unaware of which words. The existence proof is the
|
||||
above mentioned `guano`. There are almost certainly other words missing.
|
||||
|
||||
With `guano` added to `dict`, it is guessed in 3 using `blast` as the
|
||||
starting word:
|
||||
|
||||
```
|
||||
blast bbgbb
|
||||
reach bbgbb
|
||||
guano ggggg
|
||||
```
|
||||
|
||||
NB: `dict` has not been changed. `gauno` was NOT added to `dict`.
|
||||
Adding this word changes the statistics, and thus requires a change
|
||||
to all this material. It has a *slightly* negative impact, except
|
||||
for it finds the word `guano`. Essentially, as you add new words
|
||||
to `dict`, the permutations expand, and the scores decrease. This is
|
||||
why `/usr/share/dict/words` scores worse than `dict` - and loses more often.
|
||||
|
||||
April 9, 2023, this problem repeated with the target word `snafu`.
|
||||
That seems apropos.
|
||||
|
||||
```
|
||||
$ ./prog dict
|
||||
blast bbgyb
|
||||
share gbgbb
|
||||
spank gbgyb
|
||||
xyzzy
|
||||
|
||||
$ ./prog /usr/share/dict/words
|
||||
blast bbgyb
|
||||
share gbgbb
|
||||
spank gbgyb
|
||||
snafu ggggg
|
||||
```
|
||||
|
||||
And on June 9, 2023, again. This time, it cost nothing, and won on the
|
||||
second guess.
|
||||
|
||||
```
|
||||
$ ./prog dict
|
||||
blast gyygb
|
||||
xyzzy
|
||||
|
||||
$ ./prog /usr/share/dict/words
|
||||
blast gyygb
|
||||
balsa ggggg
|
||||
```
|
||||
|
||||
June 19, 2023, another one wasn't working, so time for a better dictionary:
|
||||
|
||||
```
|
||||
$ ./prog dict
|
||||
blast bbybb
|
||||
ramen bgbbb
|
||||
vapid bgbbb
|
||||
wacky bgbyb
|
||||
xyzzy
|
||||
|
||||
# a copy of /usr/share/dict/words with five letters and no capitals:
|
||||
grep '^.....$' /usr/share/dict/words | grep -v '[A-Z]' > ./usdw
|
||||
|
||||
$ ./prog usdw
|
||||
blast bbybb
|
||||
redan bbbyb
|
||||
campo bgbbg
|
||||
wahoo bgbgg
|
||||
kazoo ggggg
|
||||
```
|
||||
|
||||
As of June 19, 2023, it is known that at least these words have been added
|
||||
(more are likely). Using `./usdw` as above, `prog` guesses these words in:
|
||||
|
||||
```
|
||||
guano 3
|
||||
snafu 4
|
||||
balsa 2
|
||||
kazoo 5
|
||||
```
|
||||
|
||||
See the [extra.html](extra.html) for more details on the program `run`:
|
||||
|
||||
```
|
||||
for n in guano snafu balsa kazoo; do
|
||||
./run usdw blast $n;
|
||||
done | grep good
|
||||
```
|
||||
|
||||
As of April 22, 2024, add the word `laser` in 3 guesses.
|
||||
|
||||
```
|
||||
$ ./prog usdw
|
||||
blast byyyb
|
||||
solea ybygy
|
||||
laser ggggg
|
||||
```
|
||||
|
||||
And May 8, 2024 brings on `pious` in 4 guesses (after the failure with `dict`):
|
||||
|
||||
```
|
||||
$ prog dict
|
||||
blast bbbyb
|
||||
siren ygbbb
|
||||
ficus bgbgg
|
||||
xyzzy
|
||||
|
||||
$ prog usdw
|
||||
? blast bbbyb
|
||||
serio
|
||||
? siren ygbbb
|
||||
pisco
|
||||
? ficus bgbgg
|
||||
pious
|
||||
? pious ggggg
|
||||
```
|
||||
|
||||
Notice here that the suggested word was NOT used, instead the original
|
||||
guesses were used, and it found the correct answer in 4 guesses.
|
||||
However, doing
|
||||
|
||||
```
|
||||
./run usdw blast pious
|
||||
```
|
||||
|
||||
gets the answer correct in 3 guesses: blast > serio > pious.
|
||||
|
||||
And Sep 17, 2024 brings us the word `beaut`.
|
||||
|
||||
```
|
||||
$ prog dict
|
||||
blast gbgbg
|
||||
xyzzy
|
||||
|
||||
$ prog usdw
|
||||
blast gbgbgb
|
||||
beant -- not allowed, so removed beant from usdw
|
||||
brant gbgbg
|
||||
beaut ggggg
|
||||
```
|
||||
|
||||
Three guesses using an alternate dictionary and having to remove `beant`.
|
||||
This word isn't even a word I can find in Webster's. I hope your version
|
||||
of `/usr/share/dict/words` has more cromulent words.
|
||||
|
||||
Oct 9, 2024 brings the word `mommy`.
|
||||
|
||||
```
|
||||
$ prog dict
|
||||
blast bbbbb
|
||||
drone bbybb
|
||||
pouch bgbbb
|
||||
foggy bgbbg
|
||||
|
||||
$ prog usdw
|
||||
blast bbbbb
|
||||
drone bbybb
|
||||
pouch bgbbb
|
||||
foggy bgbbg
|
||||
mommy ggggg
|
||||
```
|
||||
|
||||
Fortunately, the program is good enough that failing with one
|
||||
dictionary still allows you to switch dictionaries and win.
|
||||
|
||||
Nov 13, 2024 brings `primp`.
|
||||
|
||||
```
|
||||
$ ./prog dict
|
||||
blast bbbbb
|
||||
drone bgbbb
|
||||
crimp bgggg
|
||||
xyzzy
|
||||
|
||||
$ ./prog usdw
|
||||
blast bbbbb
|
||||
drone bgbbb
|
||||
crimp bgggg
|
||||
primp ggggg
|
||||
```
|
||||
|
||||
Jan 20, 2025 brings the word `squid`.
|
||||
|
||||
```
|
||||
$ ./prog dict
|
||||
blast bbbyb
|
||||
siren gybbb
|
||||
skimp gbybb
|
||||
sushi <<-- last plausible word
|
||||
|
||||
$ ./prog usdw
|
||||
blast bbbyb
|
||||
siren gybbb
|
||||
skimp gbybb
|
||||
sushi sgbby
|
||||
squid ggggg
|
||||
```
|
||||
|
||||
As of Jan 20, 2025, the ten known words not in `dict` are
|
||||
(scores are using `usdw`, not `dict`, which lacks these words):
|
||||
|
||||
```
|
||||
guano 4
|
||||
snafu 5
|
||||
balsa 3
|
||||
kazoo 4
|
||||
laser 3
|
||||
pious 4
|
||||
beaut 3
|
||||
mommy 4
|
||||
primp 5
|
||||
squid 4
|
||||
```
|
||||
|
||||
Feb 18, 2025 adds a word not in `dict` nor `usdw`. "indie".
|
||||
|
||||
```
|
||||
$ ./prog dict
|
||||
blast bbbbb
|
||||
drone ybbyg
|
||||
undue bggbg
|
||||
xyzzy
|
||||
|
||||
$ ./prog usdw
|
||||
blast bbbbb
|
||||
drone ybbyg
|
||||
undue bggbg
|
||||
xyzzy
|
||||
|
||||
I grep'd the dictionaries. No plausbile hits.
|
||||
So I solved it the old fashioned way: I guessed.
|
||||
|
||||
blast bbbbb
|
||||
drone ybbyg
|
||||
undue bggbg
|
||||
indie ggggg
|
||||
|
||||
This solved it in 4, but the program could not win.
|
||||
So add to the summary list above:
|
||||
|
||||
indie 4
|
||||
```
|
||||
|
||||
|
||||
### Compilation
|
||||
|
||||
Compiles cleanly with `-Wall -Wpedantic -Wextra`,
|
||||
on MACOS M2 Clang/GCC 14 and Linux X86 Gcc 8.5.
|
||||
|
||||
If you are using `-Weverything`,
|
||||
which IMHO is exceptionally noisy about non-problems,
|
||||
and given the requirements of obfuscation and layout,
|
||||
there are a few `-Wno-` flags required to achieve a
|
||||
quiet compilation:
|
||||
|
||||
```
|
||||
-Wno-poison-system-directories \
|
||||
-Wno-missing-variable-declarations \
|
||||
-Wno-shadow \
|
||||
-Wno-vla \
|
||||
-Wno-missing-prototypes \
|
||||
-Wno-shorten-64-to-32 \
|
||||
-Wno-format-nonliteral \
|
||||
-Wno-implicit-int-conversion \
|
||||
-Wno-declaration-after-statement \
|
||||
-Wno-comma \
|
||||
-Wno-array-bounds-pointer-arithmetic \
|
||||
-Wno-sign-conversion
|
||||
```
|
||||
|
||||
The code uses variable length arrays, omitted the keyword `static`
|
||||
for functions and global variables, and collide `size_t` with `int`
|
||||
for return types. There are no pedantic prototypes for functions.
|
||||
There is no misuse of the comma operator, but try getting the compiler
|
||||
to ignore it. The code has been run on MacOS M2 and Linux x86 with
|
||||
no failures observed. And as documented in [deobfuscated.html](deobfuscated.html),
|
||||
pointer bounds are intentionally abused for obfuscation, but the
|
||||
code is correct as documented.
|
||||
This is obfuscated C, so yes, variables are intentionally shadowed.
|
||||
`printf()` is also abused, passing in non-literals as the format.
|
||||
|
||||
Perhaps there is some platform out there that won't work correctly
|
||||
due to the omissions noted above, but I am unaware of these.
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
Copyright © 1984-2025 by Landon Curt Noll and Leonid A. Broukhis. All Rights Reserved.
|
||||
|
||||
You are free to share and adapt this file under the terms of this license:
|
||||
|
||||
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
|
||||
|
||||
For more information, see:
|
||||
|
||||
https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
-->
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
awk '
|
||||
/^guess1/ {if (w == "") w = $2 }
|
||||
/good:/ {n++; t+=$5 }
|
||||
/lose:/ {n++; L++ }
|
||||
END {printf "%s %.06f %d\n",w,t/n,L }' $1
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
a=$1
|
||||
b=$2
|
||||
|
||||
if test $# -ne 2; then echo "usage: $0 result.a result.b" >&2; exit 1; fi
|
||||
|
||||
join $a $b | awk '
|
||||
{
|
||||
davg=$4-$2
|
||||
dloss=$5-$3
|
||||
print $0, davg, dloss
|
||||
}
|
||||
' | tabby
|
||||
@@ -0,0 +1,3 @@
|
||||
? amber
|
||||
? adobe
|
||||
? abode
|
||||
@@ -0,0 +1,6 @@
|
||||
? daunt
|
||||
? haunt
|
||||
? gaunt
|
||||
? vaunt
|
||||
? taunt
|
||||
? jaunt
|
||||
@@ -0,0 +1,2 @@
|
||||
? loath
|
||||
? light
|
||||
@@ -0,0 +1,165 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef void q;
|
||||
typedef int _;
|
||||
typedef char p;
|
||||
|
||||
struct {
|
||||
p a[257], b[9][5], c[5], d[5];
|
||||
_ e, f;
|
||||
} i;
|
||||
p t[78557][5];
|
||||
_ s, a[] = {
|
||||
9, 9, 9, 10, 13, 18, 27, 39, 55, 1, 18, 2, 1, 1,
|
||||
35, 2, 19, 18, 1, 52, 36, 35, 20, 3, 2, 19, 18, 18,
|
||||
69, 2, 53, 1, 52, 37, 3, 20, 36, 19, 35, 18, 52, 86,
|
||||
2, 53, 70, 1, 69, 38, /* 36 */ 4, 21, 3, 37, 19,
|
||||
36, 35, 25, 10, 17, 15, 26, 9, 11, 12, 23, 2, 6, 18,
|
||||
13, 20, 22, 14, 1, 24, 19, 21, 16, 5, 7, 4, 8, 3
|
||||
} , u = ' ';
|
||||
|
||||
_ W(p *a, p *b) {
|
||||
for (p *c = a + 5; a < c; )
|
||||
*a++ = *b++;
|
||||
return 0;
|
||||
}
|
||||
_ O(p *a, p b) {
|
||||
p *c = a + 5;
|
||||
for (; a < c && *a - b; )
|
||||
++a;
|
||||
return a < c ? 5 - (a - c) : 0;
|
||||
}
|
||||
_ R(p *a, ...) {
|
||||
va_list b;
|
||||
va_start(b, a);
|
||||
vprintf(a, b);
|
||||
exit(1);
|
||||
}
|
||||
q D(p *a) {
|
||||
if (s == sizeof t / 5)
|
||||
R("limit %d\n", s);
|
||||
p *b = s[t], i, j;
|
||||
for (i = j = 0; i < 7 && !j; ++i)
|
||||
if (!(j = *a == 10 ? i : 0))
|
||||
*b++ = u | *a++;
|
||||
j - 5 || ++s;
|
||||
}
|
||||
q L(p *a, _ b) {
|
||||
for (_ l = 0, o = 0, k; l < s; o = 0) {
|
||||
for (_ j = 0; !o && j < 5 && (k = t[l][j]); ++j)
|
||||
i.c[j] && k - i.c[j] && ++o;
|
||||
for (_ j = 0; !o && j < 5 && (k = t[l][j]); ++j)
|
||||
i.a[k] && ++o;
|
||||
for (_ j = 0; !o && j < 5 && (k = t[l][j]); ++j)
|
||||
i.d[j] && !O(t[l], i.d[j]) && ++o;
|
||||
for (_ f = i.e; f-- && !o; )
|
||||
for (_ j = 0; !o && j < 5 && (k = t[l][j]); ++j)
|
||||
i.b[f][j] == k && ++o;
|
||||
if (!o) {
|
||||
for (_ j = 0; j < 5; ++j)
|
||||
o += t[l][j] == a[j];
|
||||
o = o == 5 ? b != 5 : 0;
|
||||
}
|
||||
o ? W(t[l], t[--s]) : ++l;
|
||||
}
|
||||
}
|
||||
_ e(_ W, _ O, _ R, _ D, _ *L, _ *E) {
|
||||
for (; O < D; )
|
||||
L[R++] = E[O++ + D * W];
|
||||
return R;
|
||||
}
|
||||
q S(_ O, _ *l) {
|
||||
for (_ *s = &a[a[O]], *e = a[-~O] + s - a[O]; s < e; ) {
|
||||
_ i = *s++,
|
||||
j = i & 15,
|
||||
k = l[i >>= 4] < l[j] ? k = l[i], l[i] = l[j], l[j] = k : i;
|
||||
}
|
||||
}
|
||||
_ o(_ W, _ O, _ R, _ D, _ *L, _ *E) {
|
||||
_ o = 0, w = 0;
|
||||
for (; o < D & w < D; )
|
||||
L[R++] = E[o + D * W] >= E[w + D * O]
|
||||
? E[o++ + D * W]
|
||||
: E[w++ + D * O];
|
||||
return e(O, w, e(W, o, R, D, L, E), D, L, E);
|
||||
}
|
||||
q l(_ n, _ *f) {
|
||||
for (_ *e = f + 26, i, j; f < e && *f > u; f += i) {
|
||||
for (i = 1, j = *f / u; i < n && f[i] >> 5 == j; )
|
||||
++i;
|
||||
for (j = 0; j < i; ++j)
|
||||
f[j] &= 31,
|
||||
f[j] |= a[f[j] + a[~-9]] << 5;
|
||||
S(i, f);
|
||||
}
|
||||
}
|
||||
q v(_ a, _ *w, _ *k) {
|
||||
o(2, 3, o(0, 1, 0, a, w, k), a, w, k);
|
||||
o(0, 1, 0, a + a, k, w);
|
||||
l(a, k);
|
||||
}
|
||||
p *E(p *d) {
|
||||
_ l, j, k = u, b[u], c[k];
|
||||
p *e[97][5] = { 0 } ;
|
||||
for (l = 0; l < k; ++l)
|
||||
c[l] = l;
|
||||
for (l = 0; l < s; ++l)
|
||||
for (_ j = 0; j < 5; ++j)
|
||||
c[t[l][j] - 97] += k;
|
||||
for (l = j = 7; j < 22; j += l)
|
||||
S(l, c + j - l);
|
||||
v(l, b, c);
|
||||
for (l = 0; l < k; ++l)
|
||||
b[c[l] & ~-k] = l;
|
||||
for (k *= 3, l = 0; l < s; ++l) {
|
||||
_ i = 0;
|
||||
for (_ j = 0; j < 5; ++j)
|
||||
i += b[t[l][j] - 97];
|
||||
if (i < k)
|
||||
for (_ j = 0; j < 5; ++j)
|
||||
e[i][j] || (e[i][j] = t[l], j = 4);
|
||||
}
|
||||
for (l = 0; l < k; ++l)
|
||||
for (p *b = 0, k = j = 0; j < 5 && (b = e[l][j]); k = !++j) {
|
||||
p c[32] = { 0 } ;
|
||||
d = *d - 120 ? d : b;
|
||||
if (s > 2)
|
||||
for (_ j = 0; j < 5; ++j)
|
||||
k += ++c[b[j] - 97] > 1;
|
||||
if (!k)
|
||||
return b;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
p *r(p *b) {
|
||||
_ k, l;
|
||||
for (_ j = k = l = 0; j < 5; ++j) {
|
||||
signed char n = b[j], o = b[j - ~5] + ~0x66;
|
||||
(o ? i.b[i.e] : i.c)[j] = n;
|
||||
!o ? i.a[n] = !++k : O(i.d, n) || (*(o < 0 ? &i.a[n] : &i.d[i.f++]) = l = n);
|
||||
}
|
||||
i.e += !!l;
|
||||
L(b, k);
|
||||
return E("xyzzy");
|
||||
}
|
||||
_ main(_ n, char **v) {
|
||||
p *j = i.c - 97, *l = j - 11;
|
||||
n - 2 ? R("usage: %s dict\n", *v) : 5;
|
||||
FILE *k = fopen(*++v, "r");
|
||||
if (!k)
|
||||
return perror(*v), 1;
|
||||
while (fgets(j, u ^ n, k))
|
||||
D(j);
|
||||
fclose(k);
|
||||
setbuf(stdout, 0);
|
||||
while (~-s) {
|
||||
printf("? ");
|
||||
while (~(n = getchar()) && n - 10 && l < j)
|
||||
*l++ = n;
|
||||
l < j && R("input: guess color\n");
|
||||
printf("%.5s\n", r(l = j + ~10));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,814 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<!-- START: two lines up starts content from: inc/top.default.html -->
|
||||
|
||||
<!-- END: this line ends content from: inc/top.default.html -->
|
||||
<!-- START: this line starts content from: inc/head.default.html -->
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../ioccc.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap" rel="stylesheet">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<title>The International Obfuscated C Code Contest</title>
|
||||
<link rel="icon" type="image/x-icon" href="../../favicon.ico">
|
||||
<meta name="description" content="deobfuscated.md for 2024/burton">
|
||||
<meta name="keywords" content="IOCCC, 2024/burton, deobfuscated.md">
|
||||
</head>
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<!-- !!! DO NOT MODIFY THIS FILE - This file is generated by a tool !!! -->
|
||||
<!-- !!! DO NOT MODIFY THIS FILE - This file is generated by a tool !!! -->
|
||||
<!-- !!! DO NOT MODIFY THIS FILE - This file is generated by a tool !!! -->
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
|
||||
<!-- END: this line ends content from: inc/head.default.html -->
|
||||
|
||||
<!-- -->
|
||||
<!-- This web page was formed via the tool: bin/gen-other-html.sh -->
|
||||
<!-- The content of main section of this web page came from: 2024/burton/deobfuscated.md -->
|
||||
<!-- -->
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<!-- !!! Do not modify this web page, instead modify the file: 2024/burton/deobfuscated.md !!! -->
|
||||
<!-- !!! Do not modify this web page, instead modify the file: 2024/burton/deobfuscated.md !!! -->
|
||||
<!-- !!! Do not modify this web page, instead modify the file: 2024/burton/deobfuscated.md !!! -->
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
|
||||
<!-- Markdown content was converted into HTML via the tool: bin/md2html.sh -->
|
||||
|
||||
<!-- START: this line starts content from: inc/body.default.html -->
|
||||
|
||||
<body>
|
||||
|
||||
<!-- END: this line ends content from: inc/body.default.html -->
|
||||
<!-- START: this line starts content from: inc/topbar.default.html -->
|
||||
|
||||
<div class="theader">
|
||||
<nav class="topbar">
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<a href="../../index.html" class="logo-link">
|
||||
IOCCC
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="topbar-items">
|
||||
<div class="item">
|
||||
<span class="item-header">
|
||||
Entries
|
||||
</span>
|
||||
|
||||
<div class="sub-item">
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../years.html" class="sub-item-link">
|
||||
Winning entries
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../authors.html" class="sub-item-link">
|
||||
Winning authors
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../location.html" class="sub-item-link">
|
||||
Location of authors
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../bugs.html" class="sub-item-link">
|
||||
Bugs and (mis)features
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html#fix_an_entry" class="sub-item-link">
|
||||
Fixing entries
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html#fix_author" class="sub-item-link">
|
||||
Updating author info
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<span class="item-header">
|
||||
Status
|
||||
</span>
|
||||
|
||||
<div class="sub-item">
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../news.html" class="sub-item-link">
|
||||
News
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../status.html" class="sub-item-link">
|
||||
Contest status
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../next/rules.html" class="sub-item-link">
|
||||
IOCCC rules
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../next/guidelines.html" class="sub-item-link">
|
||||
IOCCC guidelines
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../markdown.html" class="sub-item-link">
|
||||
Markdown guidelines
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../SECURITY.html" class="sub-item-link">
|
||||
Security policy
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<span class="item-header">
|
||||
FAQ
|
||||
</span>
|
||||
|
||||
<div class="sub-item">
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html" class="sub-item-link">
|
||||
Frequently asked questions
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../quick-start.html#enter" class="sub-item-link">
|
||||
Enter the IOCCC
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html#compiling" class="sub-item-link">
|
||||
Compiling entries
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html#running_entries" class="sub-item-link">
|
||||
Running entries
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html#help" class="sub-item-link">
|
||||
How to help
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<span class="item-header">
|
||||
About
|
||||
</span>
|
||||
|
||||
<div class="sub-item">
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../index.html" class="sub-item-link">
|
||||
Home page
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../social.html" class="sub-item-link">
|
||||
IOCCC social media
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../about.html" class="sub-item-link">
|
||||
About the IOCCC
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../judges.html" class="sub-item-link">
|
||||
The judges
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../thanks-for-help.html" class="sub-item-link">
|
||||
Thanks for the help
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../contact.html" class="sub-item-link">
|
||||
Contact us
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="header-mobile-menu">
|
||||
<noscript>
|
||||
<a href="../../nojs-menu.html" class="topbar-js-label">
|
||||
Please Enable JavaScript
|
||||
</a>
|
||||
</noscript>
|
||||
|
||||
<button id="header-open-menu-button" class="topbar-mobile-menu">
|
||||
<img
|
||||
src="../../png/hamburger-icon-open.png"
|
||||
alt="hamburger style menu icon - open state"
|
||||
width=48
|
||||
height=48>
|
||||
</button>
|
||||
|
||||
<button id="header-close-menu-button" class="hide-content">
|
||||
<img
|
||||
src="../../png/hamburger-icon-closed.png"
|
||||
alt="hamburger style menu icon - closed state"
|
||||
width=48
|
||||
height=48>
|
||||
</button>
|
||||
|
||||
<div id="mobile-menu-panel" class="hide-content">
|
||||
<div class="mobile-menu-container">
|
||||
|
||||
<div class="mobile-menu-wrapper">
|
||||
<div class="mobile-menu-item">
|
||||
Entries
|
||||
</div>
|
||||
<div class="mobile-submenu-wrapper">
|
||||
|
||||
<a class="mobile-submenu-item" href="../../years.html">
|
||||
Winning entries
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../authors.html">
|
||||
Winning authors
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../location.html">
|
||||
Location of authors
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../bugs.html">
|
||||
Bugs and (mis)features
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html#fix_an_entry">
|
||||
Fixing entries
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html#fix_author">
|
||||
Updating author info
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../thanks-for-help.html">
|
||||
Thanks for the help
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mobile-menu-wrapper">
|
||||
|
||||
<div class="mobile-menu-item">
|
||||
Status
|
||||
</div>
|
||||
<div class="mobile-submenu-wrapper">
|
||||
|
||||
<a class="mobile-submenu-item" href="../../news.html">
|
||||
News
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../status.html">
|
||||
Contest status
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../next/rules.html">
|
||||
IOCCC rules
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../next/guidelines.html">
|
||||
IOCCC guidelines
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../markdown.html">
|
||||
Markdown guidelines
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../SECURITY.html">
|
||||
Security policy
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mobile-menu-wrapper">
|
||||
<div class="mobile-menu-item">
|
||||
FAQ
|
||||
</div>
|
||||
<div class="mobile-submenu-wrapper">
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html">
|
||||
Frequently asked questions
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../quick-start.html#enter">
|
||||
Enter the IOCCC
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html#compiling">
|
||||
Compiling entries
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html#running_entries">
|
||||
Running entries
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html#help">
|
||||
How to help
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mobile-menu-wrapper">
|
||||
<div class="mobile-menu-item">
|
||||
About
|
||||
</div>
|
||||
<div class="mobile-submenu-wrapper">
|
||||
|
||||
<a class="mobile-submenu-item" href="../../index.html">
|
||||
Home page
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../social.html">
|
||||
IOCCC social media
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../about.html">
|
||||
About the IOCCC
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../judges.html">
|
||||
The judges
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../contact.html">
|
||||
Contact us
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var headerOpenMenuButton = document.getElementById("header-open-menu-button");
|
||||
var headerCloseMenuButton = document.getElementById("header-close-menu-button");
|
||||
var mobileMenuPanel = document.getElementById("mobile-menu-panel");
|
||||
|
||||
headerOpenMenuButton.addEventListener("click", () => {
|
||||
headerOpenMenuButton.classList.remove("topbar-mobile-menu");
|
||||
headerOpenMenuButton.classList.add("hide-content");
|
||||
|
||||
headerCloseMenuButton.classList.remove("hide-content");
|
||||
headerCloseMenuButton.classList.add("topbar-mobile-menu");
|
||||
|
||||
mobileMenuPanel.classList.remove("hide-content");
|
||||
mobileMenuPanel.classList.add("topbar-mobile-panel");
|
||||
});
|
||||
|
||||
headerCloseMenuButton.addEventListener("click", () => {
|
||||
headerCloseMenuButton.classList.remove("topbar-mobile-menu");
|
||||
headerCloseMenuButton.classList.add("hide-content");
|
||||
|
||||
mobileMenuPanel.classList.add("hide-content");
|
||||
mobileMenuPanel.classList.remove("topbar-mobile-panel");
|
||||
|
||||
headerOpenMenuButton.classList.add("topbar-mobile-menu");
|
||||
headerOpenMenuButton.classList.remove("hide-content");
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- END: this line ends content from: inc/topbar.default.html -->
|
||||
<!-- START: this line starts content from: inc/header.default.html -->
|
||||
|
||||
<div class="header">
|
||||
<a href="../../2011/zucker/index.html">
|
||||
<img src="../../png/ioccc.png"
|
||||
alt="IOCCC image by Matt Zucker"
|
||||
width=300
|
||||
height=110>
|
||||
</a>
|
||||
<h1>The International Obfuscated C Code Contest</h1>
|
||||
<h2>deobfuscated.html for 2024/burton</h2>
|
||||
</div>
|
||||
|
||||
<!-- END: this line ends content from: inc/header.default.html -->
|
||||
<!-- START: this line starts content from: inc/navbar.up2index.html -->
|
||||
|
||||
<div class="navbar">
|
||||
<a class="Left" href="index.html">↑ jump to 2024/burton/index.html ↑</a>
|
||||
<a class="Right" href="https://validator.w3.org/nu/?doc=https%3A%2F%2Fwww.ioccc.org%2F2024%2Fburton%2Fdeobfuscated.html">✓</a>
|
||||
</div>
|
||||
|
||||
<!-- END: this line ends content from: inc/navbar.up2index.html -->
|
||||
<!-- START: this line starts content from: inc/before-content.default.html -->
|
||||
|
||||
<div class="content" id="content">
|
||||
|
||||
<!-- END: this line ends content from: inc/before-content.default.html -->
|
||||
<!-- START: this line starts content for HTML phase 21 by: bin/pandoc-wrapper.sh via bin/md2html.sh -->
|
||||
|
||||
<!-- BEFORE: 1st line of markdown file: 2024/burton/deobfuscated.md -->
|
||||
<h1 id="do-not-read-this-if-you-want-to-figure-it-out-yourself">Do not read this if you want to figure it out yourself!</h1>
|
||||
<p>The file <code>deobfuscated.c</code> is simply a pretty-printed version of
|
||||
<code>prog.c</code> with the macros expanded. It is hopefully just as
|
||||
difficult to understand as the original presentation:
|
||||
obfuscation is not just presentation, after all.</p>
|
||||
<p>If you read further, the remainder talks about how the code
|
||||
works. If you want to figure it out yourself, stop reading here
|
||||
and do look at <code>deobfuscated.c</code>.</p>
|
||||
<h2 id="read-dictionary">Read Dictionary</h2>
|
||||
<p>The program begins by reading junk ;-) from the dictionary and processing
|
||||
via <code>D()</code>. The words are stored in <code>t[][]</code>, counted by <code>s</code> and <code>D()</code> ensures
|
||||
this never has buffer overflow. Notice the swap of index and array in <code>s[t]</code>.
|
||||
It also ensures only 5 letter words are selected from the dictionary,
|
||||
and folds uppercase to lowercase. Each call of <code>D()</code> handles one word.</p>
|
||||
<p>NB: if the dictionary has, for instance, “ARRAY” and “array” and “Array”,
|
||||
there will be 3 copies of “array” stored in <code>t[][]</code>.
|
||||
This will degrade performance and may cause a malfunction of the algorithm,
|
||||
and definitely if the target word is “array”. Thus the <code>makefile</code> builds
|
||||
<code>usdw</code> with capital letters removed (avoiding this problem completely).
|
||||
However, if there are duplicate words in the dictionary, use another.</p>
|
||||
<h2 id="user-input">User Input</h2>
|
||||
<p>After the dictionary is read, it is time to prompt the user for the
|
||||
input guess. This happens in <code>main()</code>. The input buffer is the upper
|
||||
half of <code>i.a</code>, which is never used (ASCII lowercase letters only).
|
||||
The newline is ignored by <code>n - 10</code>, at most 11 characters (<code>l < j</code>)
|
||||
can be input, and EOF terminates the input loop.</p>
|
||||
<p>Here is the explanation of how <code>i.c - 97</code> works to get an input buffer.
|
||||
Assume <code>struct i</code> has the address 1000, then <code>i.a</code> begins at 1000.
|
||||
<code>i.b</code> begins at 1257. <code>i.c</code> begins at 9*5 = 45 + 1257 = 1302.
|
||||
1302 - 97 = 1205. Therefore, the <code>i.c - 97</code> sets the input buffer at
|
||||
<code>&i.a[205]</code>, ASCII lowercase input ends at <code>i.a[123]</code>, and there are
|
||||
53 characters before <code>i.b</code> is overwritten - plenty of room for the user IO.
|
||||
Why 97? The expression <code>x - 97</code> is used several times in the code,
|
||||
leading to some confusion on the reason for the value.</p>
|
||||
<p>Note that this abuse is what draws a compiler complaint, requiring:</p>
|
||||
<pre><code> -Wno-array-bounds-pointer-arithmetic</code></pre>
|
||||
<p>It is safe. It works. There is no problem here, but the compiler
|
||||
does not know this.</p>
|
||||
<h2 id="process-user-input">Process User Input</h2>
|
||||
<p>Once the user input is received, the program calls <code>r()</code>, which populates
|
||||
<code>struct i</code> with the guesses. This serves to set the patterns known
|
||||
and impossible, as well as letters used and required (remember, this is
|
||||
hard mode Wordle, which requires you to use all green letters in the
|
||||
same place for all subsequent guesses). <code>r()</code> returns the next word
|
||||
to guess via <code>E()</code> (aka choose() - see below).</p>
|
||||
<p>Within <code>r()</code>, it is important to understand the ASCII values for g, y and b.</p>
|
||||
<pre><code> b 98
|
||||
g 103
|
||||
y 121</code></pre>
|
||||
<p>Within <code>r()</code>, <code>n</code> is the guessed letter, and <code>o</code> is color - 103.
|
||||
If <code>o</code> is greater than zero (121-103), this guess is correct, but
|
||||
incorrect location (yellow). If it is less than zero (98-103),
|
||||
this guess is not possible (black). If it is zero (103-103),
|
||||
it is the correct letter in the correct location (green).</p>
|
||||
<p><code>struct i</code> can be re-written this way:</p>
|
||||
<pre><code> struct {
|
||||
char black[256]; // wrong letters guessed so far
|
||||
char words[9][5]; // words used in guesses so far
|
||||
char green[5]; // correct letters (must reuse these)
|
||||
char yellow[5]; // incorrect letters (impossible positions)
|
||||
int num_guesses;
|
||||
int num_yellow;
|
||||
} i;</code></pre>
|
||||
<p>And <code>r()</code> can be rewritten:</p>
|
||||
<pre><code> char *r(char *input) {
|
||||
int num_green = 0, l = 0;
|
||||
|
||||
for (int j = 0; j < 5; ++j) {
|
||||
char n = input[j];
|
||||
signed char o = input[j + 6] - 103; // j - ~5 = j - -6 == j + 6
|
||||
|
||||
// if this is a correct guess
|
||||
if (o == 0) {
|
||||
i.green[j] = n;
|
||||
i.black[n] = 0; // remove this from black
|
||||
++num_green;
|
||||
|
||||
} else {
|
||||
i.words[i.num_guesses][j] = n;
|
||||
|
||||
// if not already a yellow letter
|
||||
if (memchr(i.yellow, n, 5) == NULL) {
|
||||
l = n;
|
||||
if (o < 0) i.black[n] = n; // black, impossible
|
||||
else i.yellow[i.num_yellow++] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// l is 0 if all letters correct, else l = n
|
||||
i.num_guesses += l > 0 ? 1 : 0;
|
||||
filter(input, num_green);
|
||||
return choose("xyzzy");
|
||||
}</code></pre>
|
||||
<h2 id="filter">Filter</h2>
|
||||
<p>The next step is to remove the impossible words from the in-memory dictionary.
|
||||
This is handled by <code>L()</code>. <code>L()</code> iterates over all the remaining words in the
|
||||
dictionary. <code>L()</code> runs a number of filters, and if the word is plausible,
|
||||
it is skipped, and if impossible, the current word is replaced from the end
|
||||
of the list. This prevents holes from appearing in the dictionary, and keeps
|
||||
the count accurate. The important action is carried out in the expression
|
||||
<code>o ? W(t[l], t[--s]) : ++l</code>. That is, if <code>o</code> is true, the word is replaced
|
||||
by the end of the list, else the word is skipped. Note in particular the
|
||||
expressions <code>--s</code> and <code>++l</code> (decrement and replace, or skip, respectively).</p>
|
||||
<p>Said another way, think of <code>W()</code> as an analog of <code>strncpy()</code>. Thus, if <code>o</code>
|
||||
is true, then <code>strncpy(t[l], t[--s], 5)</code>, which overwrites the current word
|
||||
with the end of the list, and decrements the number of entries. Note the
|
||||
end of the list is not <code>t[s]</code>, but <code>t[s-1]</code>, thus the pre-decrement achieves
|
||||
both the desired index and decrements the count.</p>
|
||||
<h2 id="word-selection">Word Selection</h2>
|
||||
<p>After the words are filtered, then comes the hard part: selecting which word
|
||||
to guess next! This is handled by <code>E()</code>, by far the most interesting routine
|
||||
in the code.</p>
|
||||
<p>The loops in order:</p>
|
||||
<ul>
|
||||
<li>init <code>c[]</code> with ordinal values (a-z plus 6 slop) in the lower 5 bits</li>
|
||||
<li>in the higher order bits, count the frequency of all remaining letters</li>
|
||||
<li>sort by the frequency of letters in 3 sets of 7 (21 not 26!)</li>
|
||||
<li>merge the separate sorts into one via <code>v()</code> (see below on merging)</li>
|
||||
<li>for letters with same freq, re-sort by English frequency values <code>l()</code></li>
|
||||
<li>using the sorted list, put the corresponding ordinals into <code>b[]</code></li>
|
||||
<li>for each word, if scores below 97, put into <code>e[][]</code> (low score wins)</li>
|
||||
<li>allow up to 5 duplicate scores, only keep first 5 words with same score</li>
|
||||
<li>search the list for the lowest scoring word (best match)</li>
|
||||
<li>if more than 2 words remain, find first word without duplicate letters</li>
|
||||
<li>return whatever we found, if anything</li>
|
||||
</ul>
|
||||
<p>The weakness in this scheme is at least two-fold: 1) if there are more than 5
|
||||
words with the same score, words that come later are not selected, and 2) all
|
||||
words with the same score are considered the same, without regard to letter
|
||||
order or letter frequencies. I have run no experiments here,
|
||||
but I think this is an area for minor improvement.</p>
|
||||
<h2 id="network-sort">Network Sort</h2>
|
||||
<p>Network sort<a href="https://en.wikipedia.org/wiki/Sorting_network">1</a> is a simple, compact, constant time sort with minimal compares.</p>
|
||||
<p>It is obfuscated in a number of ways.
|
||||
The routine <code>S()</code> looks into the array <code>a[]</code> and determines the number of
|
||||
elements to sort via the differences in the initial values of <code>a[]</code>.
|
||||
Consider the possible number of values given in <code>O</code> from 0 (zero) to 7:
|
||||
if <code>O</code> is 0 or 1, no sort is necessary (an empty list cannot be sorted,
|
||||
and a list of one does not need to be sorted):</p>
|
||||
<pre><code> O+1 O
|
||||
a[1] - a[0] = 9 - 9 = 0
|
||||
a[2] - a[1] = 9 - 9 = 0
|
||||
a[3] - a[2] = 10 - 9 = 1</code></pre>
|
||||
<p>If <code>O</code> is 2, then only one compare and swap is necessary. Why is this?
|
||||
Consider how many compares are necessary to determine the proper order of
|
||||
variables A and B: one test and one possible swap are all that is needed.
|
||||
If A > B, swap. If A <= B, then no swap and the list is already ordered.</p>
|
||||
<p>Where is this calculation done? Address arithmetic FTW!
|
||||
<code>e = a[-~O] + s - a[O]</code>. <code>-~O</code> is the identity <code>O+1</code>.
|
||||
So this expression restated is <code>e = s + a[O+1] - a[O]</code>.
|
||||
If you look at the first numbers in <code>a[]</code> and their offsets (table 1):</p>
|
||||
<pre><code> table 1
|
||||
offsets: 0 1 2 3 4 5 6 7 8
|
||||
values : 9 9 9 10 13 18 27 39 55</code></pre>
|
||||
<p>You can see the expression selects for O in 0..7 (table 2):</p>
|
||||
<pre><code> // table 2
|
||||
a[0+1] - a[0] = 9 - 9 = 0 // 0 selects no compares
|
||||
a[1+1] - a[1] = 9 - 9 = 0 // 1 selects no compares
|
||||
a[2+1] - a[2] = 10 - 9 = 1 // 2 selects 1 compare
|
||||
a[3+1] - a[3] = 13 - 10 = 3 // 3 selects 3 compares
|
||||
a[4+1] - a[4] = 18 - 13 = 5 // 4 selects 5 compares
|
||||
a[5+1] - a[5] = 27 - 18 = 9 // 5 selects 9 compares
|
||||
a[6+1] - a[6] = 39 - 27 = 12 // 6 selects 12 compares
|
||||
a[7+1] - a[7] = 55 - 39 = 16 // 7 selects 16 compares</code></pre>
|
||||
<p>What are we comparing? <code>s</code> ranges from <code>&a[a[O]]</code> to <code>&a[a[O+1]]</code>.</p>
|
||||
<pre><code> O expr
|
||||
0 9 - 9 = 0: no compares
|
||||
1 9 - 9 = 0: no compares
|
||||
2 10 - 9 = 1: &a[a[9]] + 1 or the set { 1 }
|
||||
3 13 - 10 = 3: &a[a[10]] + 3 or the set { 18, 2, 1 }
|
||||
4 18 - 13 = 5: &a[a[13]] + 5 or the set { 1, 35, 2, 19, 18 }
|
||||
...
|
||||
7 55 - 39 = 16: &a[a[39]] + 16 or the set
|
||||
{ 18, 52, 86, 2, 53, 70, 1, 69,
|
||||
38, 4, 21, 3, 37, 19, 36, 35 }</code></pre>
|
||||
<p>So what do these numbers mean for sorting? Translate them to hex:</p>
|
||||
<pre><code> { 0x12, 0x34, 0x56, 0x02, 0x35, 0x46, 0x01, 0x45,
|
||||
0x26, 0x04, 0x15, 0x03, 0x25, 0x13, 0x24, 0x23 }</code></pre>
|
||||
<p>This compact arrangement can be split into high and low nibbles:</p>
|
||||
<pre><code> { (1,2), (3,4), (5,6), (0,2), (3,5), ... }</code></pre>
|
||||
<p>which means compare and maybe swap (keeping order low to high):</p>
|
||||
<pre><code> if (s[1] > s[2]) swap();
|
||||
if (s[3] > s[4]) swap();
|
||||
if (s[5] > s[6]) swap();
|
||||
if (s[0] > s[2]) swap();
|
||||
if (s[3] > s[5]) swap();
|
||||
....</code></pre>
|
||||
<p>For instance, when sorting 7 elements, start from <code>a[a[7]]</code> (39) until
|
||||
<code>a[a[8]]</code> (55), or 16 elements starting at <code>a[39]</code>.</p>
|
||||
<h2 id="the-array-a-explained-finally">The Array a[] Explained, Finally</h2>
|
||||
<p>The first set of 9 numbers are both the lengths AND the start and end indexes
|
||||
into the array <code>a[]</code>, as explained above in the Network Sort. The second set
|
||||
of numbers runs from indexes 9 through 54, and are the Network Sort comparison
|
||||
values. At index 55 begins the third part of the array. The final 26 values
|
||||
encode the inverse frequency of letters in the English language. The first
|
||||
value is 25 for the letter ‘a’. If you count, then 26 is for the letter ‘e’,
|
||||
the most common letter in the English language
|
||||
(or at least for the dictionary used).</p>
|
||||
<p>This final set of letters is used in the routine <code>l()</code>. Notice the expression:
|
||||
<code>a[f[j] + a[~-9]]</code>; <code>a[~-9]</code> is an obscure way of writing <code>a[8]</code>, which is the
|
||||
value 55. So <code>a[f[j]]</code> is an index into this frequency array, offset by 55.
|
||||
Why is there a <code><< 5</code>? Again, the letter offsets are in the lowest 5 bits,
|
||||
and the counts of the letters is in the upper bits.</p>
|
||||
<p>The reason for putting the counts in the upper bits is simple: it makes the
|
||||
value larger for larger counts. Thus, sorting based upon frequency is natural.</p>
|
||||
<h2 id="merge-sort">Merge Sort</h2>
|
||||
<p>The network sort as implemented handles up to 7 letters in 3 calls.
|
||||
26 / 7 = 3 r 5. Thus, we need to merge up to 4 sets of 7 letters.
|
||||
This merge is handled by <code>v()</code>, which calls <code>o()</code> 3 times, once recursively.
|
||||
This is followed by a rearrangement handled in <code>l()</code>. This last step finds
|
||||
all the letters with the same frequency, then sorts them according to their
|
||||
English frequency.</p>
|
||||
<p>Back to the merge sort, which is really handled in <code>o()</code>. This is rather
|
||||
difficult to follow. The first call to <code>o()</code> is within the argument list
|
||||
to the second call. The first call has arguments
|
||||
W=0, O=1, R=0, D=a, L=w, E=k; and <code>v()</code> was called with <code>v(l=7, w=b, k=c)</code>.
|
||||
This first merge pass copies to: <code>L[R]</code> (<code>b[0]</code>) and selects from either
|
||||
<code>c[o]</code> or <code>c[w]</code> depending upon which is greater or equal. That is, it merges
|
||||
two 7 letter arrays into a single 14 element array twice, then it merges two 14
|
||||
element arrays into a single 28 element array. Note on the third call
|
||||
to <code>o()</code>, the last two arguments are swapped, which puts the final sort
|
||||
into <code>c[]</code>.</p>
|
||||
<p>Below is a hopefully easier way to read the merge sort code:</p>
|
||||
<pre><code> // merge residual
|
||||
int e(int mult_o, int start, int to, int off, int *a1, int *a2)
|
||||
{
|
||||
for (; start < off; )
|
||||
a1[to++] = a2[start++ + off * mult_o];
|
||||
return to;
|
||||
}
|
||||
|
||||
// merge sort
|
||||
int o(int mult_o, int mult_w, int to, int len, int *a1, int *a2)
|
||||
{
|
||||
// first call: mult_o = 0, mult_w = 1, to = 0, len = 7, a1=b, a2=c
|
||||
// second: mult_o = 2, mult_w = 3, to = N, len = 7, a1=b, a2=c
|
||||
// third: mult_o = 0, mult_w = 1, to = 0, len = 14, a1=c, a2=b
|
||||
|
||||
int o = 0, w = 0;
|
||||
for (; o < len && w < len; ) {
|
||||
|
||||
// first call: to=0, len=7, mult_o=0, mult_w=1
|
||||
// a1[0++] = a2[0 + 7*0] >= a2[0 + 7*1]
|
||||
// ? a2[0 + 7*0] : a2[0 + 7*1]
|
||||
// a1[0++] = a2[0] > a2[7] ? a[0++] : a2[7++]
|
||||
|
||||
// second call: to=14, len=7, mult_o=2, mult_w=3
|
||||
// a1[14++] = a2[0 + 7*2] >= a2[0 + 7*3]
|
||||
// ? a2[0 + 7*2] : a2[0 + 7*3]
|
||||
// a1[14++] = a2[14] > a2[21] ? a2[14++] : a2[21++]
|
||||
|
||||
// third call: to=0, len=14, mult_o=0, mult_w=1
|
||||
// a1[0++] = a2[0 + 14*0] >= a2[0 + 14*1]
|
||||
// ? a2[0 + 14*0] : a2[0 + 14*1];
|
||||
// a1[0++] = a[0] >= a2[14] ? a2[0++] : a2[14++]
|
||||
|
||||
a1[to++] = a2[o + len * mult_o] >= a2[w + len * mult_w]
|
||||
? a2[o++ + len * mult_o]
|
||||
: a2[w++ + len * mult_w]
|
||||
}
|
||||
int n = e(mult_o, o, to, len, a1, a2);
|
||||
return e(mult_w, w, n, len, a1, a2);
|
||||
}</code></pre>
|
||||
<p>Isn’t that as clear as mud? It is far easier to understand the flow when
|
||||
you add <code>printf()</code> statements to follow the logic. Included is the file
|
||||
<code>deobfuscated.c</code> for you to start with.</p>
|
||||
<h2 id="why-name-the-functions-as-they-are">Why name the functions as they are?</h2>
|
||||
<p>Run <code>make spoiler</code>.</p>
|
||||
<h2 id="bug-explained">Bug Explained</h2>
|
||||
<p>If you followed the above explanation, you will notice that only the first
|
||||
21 of 26 letters are sorted (ignoring { v, w, x, y, z }. But 28 of 26 letters
|
||||
are merge sorted. It was observed the program performs worse if all 26 letters
|
||||
are sorted (which tends to over-favor <code>y</code>).
|
||||
But some words do have <code>y</code> in them, which is why the merge sort takes 28
|
||||
letters (including the ASCII values <code>{</code> and <code>|</code>), so all letters are still
|
||||
selected when appropriate. The merge sort puts the { v, w, x, y, z }
|
||||
letters into the proper frequency set, eventually.</p>
|
||||
<p>If you change the loop value <code>22</code> to <code>32</code>, then all letters are sorted,
|
||||
the program will work correctly, but it will change the performance
|
||||
of the algorithm to be slightly worse than the original.</p>
|
||||
<p>If you change any values in the array <code>a[]</code>,
|
||||
the program will no longer work correctly.</p>
|
||||
<p>Finally, what is the significance of the array size 78557?
|
||||
<code>usdw</code> only needs 8506. Why was such a large value used?
|
||||
<!-- AFTER: last line of markdown file: 2024/burton/deobfuscated.md --></p>
|
||||
|
||||
<!-- END: this line ends content for HTML phase 21 by: bin/pandoc-wrapper.sh via bin/md2html.sh -->
|
||||
<!-- START: this line starts content from: inc/after-content.default.html -->
|
||||
|
||||
</div>
|
||||
|
||||
<!-- END: this line ends content from: inc/after-content.default.html -->
|
||||
<!-- START: this line starts content from: inc/footer.default.html -->
|
||||
|
||||
<!--
|
||||
|
||||
Copyright © 1984-2025 by Landon Curt Noll. All Rights Reserved.
|
||||
|
||||
You are free to share and adapt this file under the terms of this license:
|
||||
|
||||
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
|
||||
|
||||
For more information, see:
|
||||
|
||||
https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
-->
|
||||
|
||||
<div class="footer">
|
||||
|
||||
<div id="copyright"><h3>Copyright © 1984-2025 by Landon Curt Noll:
|
||||
<a href="https://creativecommons.org/faq/#what-does-some-rights-reserved-mean"
|
||||
target="_blank"
|
||||
rel="license noopener noreferrer">Some Rights Reserved</a></h3>
|
||||
<p>
|
||||
This work is <b>licensed by Landon Curt Noll</b> under
|
||||
<b><a href="https://creativecommons.org/licenses/by-sa/4.0/?ref=chooser-v1"
|
||||
target="_blank"
|
||||
rel="license noopener noreferrer"
|
||||
style="display:inline-block;">CC BY-SA 4.0</a></b>.
|
||||
<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;"
|
||||
src="../../png/cc.png"
|
||||
alt="cc inside circle symbol">
|
||||
<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;"
|
||||
src="../../png/by.png"
|
||||
alt="person inside circle symbol">
|
||||
<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;"
|
||||
src="../../png/sa.png"
|
||||
alt="arrow looping back on itself inside circle symbol"><br>
|
||||
You should <b>carefully review</b> the
|
||||
<b><a href="https://creativecommons.org/licenses/by-sa/4.0/legalcode.en"
|
||||
target="_blank"
|
||||
rel="license noopener noreferrer">CC BY-SA 4.0 LEGAL CODE</a></b>
|
||||
before using the licensed material.<br>
|
||||
You may wish to review the
|
||||
<b><a href="../../license.html"
|
||||
target="_blank"
|
||||
rel="license noopener noreferrer">highlights of some of the key features and terms</a></b>
|
||||
of <b>CC BY-SA 4.0</b>.<br>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="coda"><h3>Coda</h3>
|
||||
<p>
|
||||
<a href="https://validator.w3.org/nu/?doc=https%3A%2F%2Fwww.ioccc.org%2F2024%2Fburton%2Fdeobfuscated.html" rel="nofollow">Nu HTML check this web page</a><br>
|
||||
<a href="#top">Jump to top</a> <a href="#content">Jump to Content</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- END: this line ends content from: inc/footer.default.html -->
|
||||
<!-- START: this line starts content from: inc/bottom.default.html -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<!-- END: this line ends content from: inc/bottom.default.html -->
|
||||
@@ -0,0 +1,382 @@
|
||||
# Do not read this if you want to figure it out yourself! #
|
||||
|
||||
The file `deobfuscated.c` is simply a pretty-printed version of
|
||||
`prog.c` with the macros expanded. It is hopefully just as
|
||||
difficult to understand as the original presentation:
|
||||
obfuscation is not just presentation, after all.
|
||||
|
||||
If you read further, the remainder talks about how the code
|
||||
works. If you want to figure it out yourself, stop reading here
|
||||
and do look at `deobfuscated.c`.
|
||||
|
||||
|
||||
## Read Dictionary ##
|
||||
|
||||
The program begins by reading junk ;-) from the dictionary and processing
|
||||
via `D()`. The words are stored in `t[][]`, counted by `s` and `D()` ensures
|
||||
this never has buffer overflow. Notice the swap of index and array in `s[t]`.
|
||||
It also ensures only 5 letter words are selected from the dictionary,
|
||||
and folds uppercase to lowercase. Each call of `D()` handles one word.
|
||||
|
||||
NB: if the dictionary has, for instance, "ARRAY" and "array" and "Array",
|
||||
there will be 3 copies of "array" stored in `t[][]`.
|
||||
This will degrade performance and may cause a malfunction of the algorithm,
|
||||
and definitely if the target word is "array". Thus the `makefile` builds
|
||||
`usdw` with capital letters removed (avoiding this problem completely).
|
||||
However, if there are duplicate words in the dictionary, use another.
|
||||
|
||||
|
||||
## User Input ##
|
||||
|
||||
After the dictionary is read, it is time to prompt the user for the
|
||||
input guess. This happens in `main()`. The input buffer is the upper
|
||||
half of `i.a`, which is never used (ASCII lowercase letters only).
|
||||
The newline is ignored by `n - 10`, at most 11 characters (`l < j`)
|
||||
can be input, and EOF terminates the input loop.
|
||||
|
||||
Here is the explanation of how `i.c - 97` works to get an input buffer.
|
||||
Assume `struct i` has the address 1000, then `i.a` begins at 1000.
|
||||
`i.b` begins at 1257. `i.c` begins at 9*5 = 45 + 1257 = 1302.
|
||||
1302 - 97 = 1205. Therefore, the `i.c - 97` sets the input buffer at
|
||||
`&i.a[205]`, ASCII lowercase input ends at `i.a[123]`, and there are
|
||||
53 characters before `i.b` is overwritten - plenty of room for the user IO.
|
||||
Why 97? The expression `x - 97` is used several times in the code,
|
||||
leading to some confusion on the reason for the value.
|
||||
|
||||
Note that this abuse is what draws a compiler complaint, requiring:
|
||||
|
||||
```
|
||||
-Wno-array-bounds-pointer-arithmetic
|
||||
```
|
||||
|
||||
It is safe. It works. There is no problem here, but the compiler
|
||||
does not know this.
|
||||
|
||||
|
||||
## Process User Input ##
|
||||
|
||||
Once the user input is received, the program calls `r()`, which populates
|
||||
`struct i` with the guesses. This serves to set the patterns known
|
||||
and impossible, as well as letters used and required (remember, this is
|
||||
hard mode Wordle, which requires you to use all green letters in the
|
||||
same place for all subsequent guesses). `r()` returns the next word
|
||||
to guess via `E()` (aka choose() - see below).
|
||||
|
||||
Within `r()`, it is important to understand the ASCII values for g, y and b.
|
||||
|
||||
```
|
||||
b 98
|
||||
g 103
|
||||
y 121
|
||||
```
|
||||
|
||||
Within `r()`, `n` is the guessed letter, and `o` is color - 103.
|
||||
If `o` is greater than zero (121-103), this guess is correct, but
|
||||
incorrect location (yellow). If it is less than zero (98-103),
|
||||
this guess is not possible (black). If it is zero (103-103),
|
||||
it is the correct letter in the correct location (green).
|
||||
|
||||
`struct i` can be re-written this way:
|
||||
|
||||
``` <!---c-->
|
||||
struct {
|
||||
char black[256]; // wrong letters guessed so far
|
||||
char words[9][5]; // words used in guesses so far
|
||||
char green[5]; // correct letters (must reuse these)
|
||||
char yellow[5]; // incorrect letters (impossible positions)
|
||||
int num_guesses;
|
||||
int num_yellow;
|
||||
} i;
|
||||
```
|
||||
|
||||
And `r()` can be rewritten:
|
||||
|
||||
``` <!---c-->
|
||||
char *r(char *input) {
|
||||
int num_green = 0, l = 0;
|
||||
|
||||
for (int j = 0; j < 5; ++j) {
|
||||
char n = input[j];
|
||||
signed char o = input[j + 6] - 103; // j - ~5 = j - -6 == j + 6
|
||||
|
||||
// if this is a correct guess
|
||||
if (o == 0) {
|
||||
i.green[j] = n;
|
||||
i.black[n] = 0; // remove this from black
|
||||
++num_green;
|
||||
|
||||
} else {
|
||||
i.words[i.num_guesses][j] = n;
|
||||
|
||||
// if not already a yellow letter
|
||||
if (memchr(i.yellow, n, 5) == NULL) {
|
||||
l = n;
|
||||
if (o < 0) i.black[n] = n; // black, impossible
|
||||
else i.yellow[i.num_yellow++] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// l is 0 if all letters correct, else l = n
|
||||
i.num_guesses += l > 0 ? 1 : 0;
|
||||
filter(input, num_green);
|
||||
return choose("xyzzy");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Filter ##
|
||||
|
||||
The next step is to remove the impossible words from the in-memory dictionary.
|
||||
This is handled by `L()`. `L()` iterates over all the remaining words in the
|
||||
dictionary. `L()` runs a number of filters, and if the word is plausible,
|
||||
it is skipped, and if impossible, the current word is replaced from the end
|
||||
of the list. This prevents holes from appearing in the dictionary, and keeps
|
||||
the count accurate. The important action is carried out in the expression
|
||||
`o ? W(t[l], t[--s]) : ++l`. That is, if `o` is true, the word is replaced
|
||||
by the end of the list, else the word is skipped. Note in particular the
|
||||
expressions `--s` and `++l` (decrement and replace, or skip, respectively).
|
||||
|
||||
Said another way, think of `W()` as an analog of `strncpy()`. Thus, if `o`
|
||||
is true, then `strncpy(t[l], t[--s], 5)`, which overwrites the current word
|
||||
with the end of the list, and decrements the number of entries. Note the
|
||||
end of the list is not `t[s]`, but `t[s-1]`, thus the pre-decrement achieves
|
||||
both the desired index and decrements the count.
|
||||
|
||||
|
||||
## Word Selection ##
|
||||
|
||||
After the words are filtered, then comes the hard part: selecting which word
|
||||
to guess next! This is handled by `E()`, by far the most interesting routine
|
||||
in the code.
|
||||
|
||||
The loops in order:
|
||||
|
||||
- init `c[]` with ordinal values (a-z plus 6 slop) in the lower 5 bits
|
||||
- in the higher order bits, count the frequency of all remaining letters
|
||||
- sort by the frequency of letters in 3 sets of 7 (21 not 26!)
|
||||
- merge the separate sorts into one via `v()` (see below on merging)
|
||||
- for letters with same freq, re-sort by English frequency values `l()`
|
||||
- using the sorted list, put the corresponding ordinals into `b[]`
|
||||
- for each word, if scores below 97, put into `e[][]` (low score wins)
|
||||
- allow up to 5 duplicate scores, only keep first 5 words with same score
|
||||
- search the list for the lowest scoring word (best match)
|
||||
- if more than 2 words remain, find first word without duplicate letters
|
||||
- return whatever we found, if anything
|
||||
|
||||
The weakness in this scheme is at least two-fold: 1) if there are more than 5
|
||||
words with the same score, words that come later are not selected, and 2) all
|
||||
words with the same score are considered the same, without regard to letter
|
||||
order or letter frequencies. I have run no experiments here,
|
||||
but I think this is an area for minor improvement.
|
||||
|
||||
|
||||
## Network Sort ##
|
||||
|
||||
Network sort[1] is a simple, compact, constant time sort with minimal compares.
|
||||
|
||||
It is obfuscated in a number of ways.
|
||||
The routine `S()` looks into the array `a[]` and determines the number of
|
||||
elements to sort via the differences in the initial values of `a[]`.
|
||||
Consider the possible number of values given in `O` from 0 (zero) to 7:
|
||||
if `O` is 0 or 1, no sort is necessary (an empty list cannot be sorted,
|
||||
and a list of one does not need to be sorted):
|
||||
|
||||
```
|
||||
O+1 O
|
||||
a[1] - a[0] = 9 - 9 = 0
|
||||
a[2] - a[1] = 9 - 9 = 0
|
||||
a[3] - a[2] = 10 - 9 = 1
|
||||
```
|
||||
|
||||
If `O` is 2, then only one compare and swap is necessary. Why is this?
|
||||
Consider how many compares are necessary to determine the proper order of
|
||||
variables A and B: one test and one possible swap are all that is needed.
|
||||
If A > B, swap. If A <= B, then no swap and the list is already ordered.
|
||||
|
||||
Where is this calculation done? Address arithmetic FTW!
|
||||
`e = a[-~O] + s - a[O]`. `-~O` is the identity `O+1`.
|
||||
So this expression restated is `e = s + a[O+1] - a[O]`.
|
||||
If you look at the first numbers in `a[]` and their offsets (table 1):
|
||||
|
||||
```
|
||||
table 1
|
||||
offsets: 0 1 2 3 4 5 6 7 8
|
||||
values : 9 9 9 10 13 18 27 39 55
|
||||
```
|
||||
|
||||
You can see the expression selects for O in 0..7 (table 2):
|
||||
|
||||
``` <!---c-->
|
||||
// table 2
|
||||
a[0+1] - a[0] = 9 - 9 = 0 // 0 selects no compares
|
||||
a[1+1] - a[1] = 9 - 9 = 0 // 1 selects no compares
|
||||
a[2+1] - a[2] = 10 - 9 = 1 // 2 selects 1 compare
|
||||
a[3+1] - a[3] = 13 - 10 = 3 // 3 selects 3 compares
|
||||
a[4+1] - a[4] = 18 - 13 = 5 // 4 selects 5 compares
|
||||
a[5+1] - a[5] = 27 - 18 = 9 // 5 selects 9 compares
|
||||
a[6+1] - a[6] = 39 - 27 = 12 // 6 selects 12 compares
|
||||
a[7+1] - a[7] = 55 - 39 = 16 // 7 selects 16 compares
|
||||
```
|
||||
|
||||
What are we comparing? `s` ranges from `&a[a[O]]` to `&a[a[O+1]]`.
|
||||
|
||||
```
|
||||
O expr
|
||||
0 9 - 9 = 0: no compares
|
||||
1 9 - 9 = 0: no compares
|
||||
2 10 - 9 = 1: &a[a[9]] + 1 or the set { 1 }
|
||||
3 13 - 10 = 3: &a[a[10]] + 3 or the set { 18, 2, 1 }
|
||||
4 18 - 13 = 5: &a[a[13]] + 5 or the set { 1, 35, 2, 19, 18 }
|
||||
...
|
||||
7 55 - 39 = 16: &a[a[39]] + 16 or the set
|
||||
{ 18, 52, 86, 2, 53, 70, 1, 69,
|
||||
38, 4, 21, 3, 37, 19, 36, 35 }
|
||||
```
|
||||
|
||||
So what do these numbers mean for sorting? Translate them to hex:
|
||||
|
||||
``` <!---c-->
|
||||
{ 0x12, 0x34, 0x56, 0x02, 0x35, 0x46, 0x01, 0x45,
|
||||
0x26, 0x04, 0x15, 0x03, 0x25, 0x13, 0x24, 0x23 }
|
||||
```
|
||||
|
||||
This compact arrangement can be split into high and low nibbles:
|
||||
|
||||
```
|
||||
{ (1,2), (3,4), (5,6), (0,2), (3,5), ... }
|
||||
```
|
||||
|
||||
which means compare and maybe swap (keeping order low to high):
|
||||
|
||||
``` <!---c-->
|
||||
if (s[1] > s[2]) swap();
|
||||
if (s[3] > s[4]) swap();
|
||||
if (s[5] > s[6]) swap();
|
||||
if (s[0] > s[2]) swap();
|
||||
if (s[3] > s[5]) swap();
|
||||
....
|
||||
```
|
||||
|
||||
For instance, when sorting 7 elements, start from `a[a[7]]` (39) until
|
||||
`a[a[8]]` (55), or 16 elements starting at `a[39]`.
|
||||
|
||||
[1]: https://en.wikipedia.org/wiki/Sorting_network
|
||||
|
||||
|
||||
## The Array a[] Explained, Finally ##
|
||||
|
||||
The first set of 9 numbers are both the lengths AND the start and end indexes
|
||||
into the array `a[]`, as explained above in the Network Sort. The second set
|
||||
of numbers runs from indexes 9 through 54, and are the Network Sort comparison
|
||||
values. At index 55 begins the third part of the array. The final 26 values
|
||||
encode the inverse frequency of letters in the English language. The first
|
||||
value is 25 for the letter 'a'. If you count, then 26 is for the letter 'e',
|
||||
the most common letter in the English language
|
||||
(or at least for the dictionary used).
|
||||
|
||||
This final set of letters is used in the routine `l()`. Notice the expression:
|
||||
`a[f[j] + a[~-9]]`; `a[~-9]` is an obscure way of writing `a[8]`, which is the
|
||||
value 55. So `a[f[j]]` is an index into this frequency array, offset by 55.
|
||||
Why is there a `<< 5`? Again, the letter offsets are in the lowest 5 bits,
|
||||
and the counts of the letters is in the upper bits.
|
||||
|
||||
The reason for putting the counts in the upper bits is simple: it makes the
|
||||
value larger for larger counts. Thus, sorting based upon frequency is natural.
|
||||
|
||||
|
||||
## Merge Sort ##
|
||||
|
||||
The network sort as implemented handles up to 7 letters in 3 calls.
|
||||
26 / 7 = 3 r 5. Thus, we need to merge up to 4 sets of 7 letters.
|
||||
This merge is handled by `v()`, which calls `o()` 3 times, once recursively.
|
||||
This is followed by a rearrangement handled in `l()`. This last step finds
|
||||
all the letters with the same frequency, then sorts them according to their
|
||||
English frequency.
|
||||
|
||||
Back to the merge sort, which is really handled in `o()`. This is rather
|
||||
difficult to follow. The first call to `o()` is within the argument list
|
||||
to the second call. The first call has arguments
|
||||
W=0, O=1, R=0, D=a, L=w, E=k; and `v()` was called with `v(l=7, w=b, k=c)`.
|
||||
This first merge pass copies to: `L[R]` (`b[0]`) and selects from either
|
||||
`c[o]` or `c[w]` depending upon which is greater or equal. That is, it merges
|
||||
two 7 letter arrays into a single 14 element array twice, then it merges two 14
|
||||
element arrays into a single 28 element array. Note on the third call
|
||||
to `o()`, the last two arguments are swapped, which puts the final sort
|
||||
into `c[]`.
|
||||
|
||||
Below is a hopefully easier way to read the merge sort code:
|
||||
|
||||
``` <!---c-->
|
||||
// merge residual
|
||||
int e(int mult_o, int start, int to, int off, int *a1, int *a2)
|
||||
{
|
||||
for (; start < off; )
|
||||
a1[to++] = a2[start++ + off * mult_o];
|
||||
return to;
|
||||
}
|
||||
|
||||
// merge sort
|
||||
int o(int mult_o, int mult_w, int to, int len, int *a1, int *a2)
|
||||
{
|
||||
// first call: mult_o = 0, mult_w = 1, to = 0, len = 7, a1=b, a2=c
|
||||
// second: mult_o = 2, mult_w = 3, to = N, len = 7, a1=b, a2=c
|
||||
// third: mult_o = 0, mult_w = 1, to = 0, len = 14, a1=c, a2=b
|
||||
|
||||
int o = 0, w = 0;
|
||||
for (; o < len && w < len; ) {
|
||||
|
||||
// first call: to=0, len=7, mult_o=0, mult_w=1
|
||||
// a1[0++] = a2[0 + 7*0] >= a2[0 + 7*1]
|
||||
// ? a2[0 + 7*0] : a2[0 + 7*1]
|
||||
// a1[0++] = a2[0] > a2[7] ? a[0++] : a2[7++]
|
||||
|
||||
// second call: to=14, len=7, mult_o=2, mult_w=3
|
||||
// a1[14++] = a2[0 + 7*2] >= a2[0 + 7*3]
|
||||
// ? a2[0 + 7*2] : a2[0 + 7*3]
|
||||
// a1[14++] = a2[14] > a2[21] ? a2[14++] : a2[21++]
|
||||
|
||||
// third call: to=0, len=14, mult_o=0, mult_w=1
|
||||
// a1[0++] = a2[0 + 14*0] >= a2[0 + 14*1]
|
||||
// ? a2[0 + 14*0] : a2[0 + 14*1];
|
||||
// a1[0++] = a[0] >= a2[14] ? a2[0++] : a2[14++]
|
||||
|
||||
a1[to++] = a2[o + len * mult_o] >= a2[w + len * mult_w]
|
||||
? a2[o++ + len * mult_o]
|
||||
: a2[w++ + len * mult_w]
|
||||
}
|
||||
int n = e(mult_o, o, to, len, a1, a2);
|
||||
return e(mult_w, w, n, len, a1, a2);
|
||||
}
|
||||
```
|
||||
|
||||
Isn't that as clear as mud? It is far easier to understand the flow when
|
||||
you add `printf()` statements to follow the logic. Included is the file
|
||||
`deobfuscated.c` for you to start with.
|
||||
|
||||
|
||||
## Why name the functions as they are? ##
|
||||
|
||||
Run `make spoiler`.
|
||||
|
||||
|
||||
## Bug Explained ##
|
||||
|
||||
If you followed the above explanation, you will notice that only the first
|
||||
21 of 26 letters are sorted (ignoring { v, w, x, y, z }. But 28 of 26 letters
|
||||
are merge sorted. It was observed the program performs worse if all 26 letters
|
||||
are sorted (which tends to over-favor `y`).
|
||||
But some words do have `y` in them, which is why the merge sort takes 28
|
||||
letters (including the ASCII values `{` and `|`), so all letters are still
|
||||
selected when appropriate. The merge sort puts the { v, w, x, y, z }
|
||||
letters into the proper frequency set, eventually.
|
||||
|
||||
If you change the loop value `22` to `32`, then all letters are sorted,
|
||||
the program will work correctly, but it will change the performance
|
||||
of the algorithm to be slightly worse than the original.
|
||||
|
||||
If you change any values in the array `a[]`,
|
||||
the program will no longer work correctly.
|
||||
|
||||
Finally, what is the significance of the array size 78557?
|
||||
`usdw` only needs 8506. Why was such a large value used?
|
||||
+2315
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* usage: dups [-n] [len]
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
int no = ac > 1 && av[1][0] == '-' && av[1][1] == 'n' && ++av && --ac;
|
||||
int n = ac > 1 ? atoi(av[1]) : 5;
|
||||
|
||||
while (fgets(buf, sizeof buf, stdin)) {
|
||||
char d[256] = {0};
|
||||
int skip = 0;
|
||||
if (buf[n] != '\n')
|
||||
continue;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int c = buf[i] |= 32;
|
||||
if (++d[c] > 1)
|
||||
++skip;
|
||||
}
|
||||
if (!!skip != no)
|
||||
printf("%s", buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# amount of parallelism
|
||||
p=8
|
||||
|
||||
dir=$1
|
||||
: ${dir:=results}
|
||||
mkdir -p $dir
|
||||
|
||||
start_words=$2
|
||||
: ${start_words:=start_words}
|
||||
|
||||
dict=$3
|
||||
: ${dict:=dict}
|
||||
|
||||
rm -fr ./tmp
|
||||
mkdir -p tmp
|
||||
|
||||
# split dict into smaller chucks for some parallelism
|
||||
n=1
|
||||
cat $start_words | ./dups -n | while read a; do
|
||||
echo $a >> tmp/$n
|
||||
((n+=1))
|
||||
test $n -gt $p && n=1
|
||||
done
|
||||
|
||||
# run the experiment (expensive)
|
||||
n=1
|
||||
while test $n -le $p; do
|
||||
./word.result $dir $dict < tmp/$n 2>/dev/null &
|
||||
((n+=1))
|
||||
done
|
||||
|
||||
rm -fr ./tmp
|
||||
|
||||
wait
|
||||
@@ -0,0 +1,887 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<!-- START: two lines up starts content from: inc/top.default.html -->
|
||||
|
||||
<!-- END: this line ends content from: inc/top.default.html -->
|
||||
<!-- START: this line starts content from: inc/head.default.html -->
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../ioccc.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap" rel="stylesheet">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<title>The International Obfuscated C Code Contest</title>
|
||||
<link rel="icon" type="image/x-icon" href="../../favicon.ico">
|
||||
<meta name="description" content="extra.md for 2024/burton">
|
||||
<meta name="keywords" content="IOCCC, 2024/burton, extra.md">
|
||||
</head>
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<!-- !!! DO NOT MODIFY THIS FILE - This file is generated by a tool !!! -->
|
||||
<!-- !!! DO NOT MODIFY THIS FILE - This file is generated by a tool !!! -->
|
||||
<!-- !!! DO NOT MODIFY THIS FILE - This file is generated by a tool !!! -->
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
|
||||
<!-- END: this line ends content from: inc/head.default.html -->
|
||||
|
||||
<!-- -->
|
||||
<!-- This web page was formed via the tool: bin/gen-other-html.sh -->
|
||||
<!-- The content of main section of this web page came from: 2024/burton/extra.md -->
|
||||
<!-- -->
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<!-- !!! Do not modify this web page, instead modify the file: 2024/burton/extra.md !!! -->
|
||||
<!-- !!! Do not modify this web page, instead modify the file: 2024/burton/extra.md !!! -->
|
||||
<!-- !!! Do not modify this web page, instead modify the file: 2024/burton/extra.md !!! -->
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
|
||||
<!-- Markdown content was converted into HTML via the tool: bin/md2html.sh -->
|
||||
|
||||
<!-- START: this line starts content from: inc/body.default.html -->
|
||||
|
||||
<body>
|
||||
|
||||
<!-- END: this line ends content from: inc/body.default.html -->
|
||||
<!-- START: this line starts content from: inc/topbar.default.html -->
|
||||
|
||||
<div class="theader">
|
||||
<nav class="topbar">
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<a href="../../index.html" class="logo-link">
|
||||
IOCCC
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="topbar-items">
|
||||
<div class="item">
|
||||
<span class="item-header">
|
||||
Entries
|
||||
</span>
|
||||
|
||||
<div class="sub-item">
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../years.html" class="sub-item-link">
|
||||
Winning entries
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../authors.html" class="sub-item-link">
|
||||
Winning authors
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../location.html" class="sub-item-link">
|
||||
Location of authors
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../bugs.html" class="sub-item-link">
|
||||
Bugs and (mis)features
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html#fix_an_entry" class="sub-item-link">
|
||||
Fixing entries
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html#fix_author" class="sub-item-link">
|
||||
Updating author info
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<span class="item-header">
|
||||
Status
|
||||
</span>
|
||||
|
||||
<div class="sub-item">
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../news.html" class="sub-item-link">
|
||||
News
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../status.html" class="sub-item-link">
|
||||
Contest status
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../next/rules.html" class="sub-item-link">
|
||||
IOCCC rules
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../next/guidelines.html" class="sub-item-link">
|
||||
IOCCC guidelines
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../markdown.html" class="sub-item-link">
|
||||
Markdown guidelines
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../SECURITY.html" class="sub-item-link">
|
||||
Security policy
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<span class="item-header">
|
||||
FAQ
|
||||
</span>
|
||||
|
||||
<div class="sub-item">
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html" class="sub-item-link">
|
||||
Frequently asked questions
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../quick-start.html#enter" class="sub-item-link">
|
||||
Enter the IOCCC
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html#compiling" class="sub-item-link">
|
||||
Compiling entries
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html#running_entries" class="sub-item-link">
|
||||
Running entries
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../faq.html#help" class="sub-item-link">
|
||||
How to help
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<span class="item-header">
|
||||
About
|
||||
</span>
|
||||
|
||||
<div class="sub-item">
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../index.html" class="sub-item-link">
|
||||
Home page
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../social.html" class="sub-item-link">
|
||||
IOCCC social media
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../about.html" class="sub-item-link">
|
||||
About the IOCCC
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../judges.html" class="sub-item-link">
|
||||
The judges
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../thanks-for-help.html" class="sub-item-link">
|
||||
Thanks for the help
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="outfit-font">
|
||||
<a href="../../contact.html" class="sub-item-link">
|
||||
Contact us
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="header-mobile-menu">
|
||||
<noscript>
|
||||
<a href="../../nojs-menu.html" class="topbar-js-label">
|
||||
Please Enable JavaScript
|
||||
</a>
|
||||
</noscript>
|
||||
|
||||
<button id="header-open-menu-button" class="topbar-mobile-menu">
|
||||
<img
|
||||
src="../../png/hamburger-icon-open.png"
|
||||
alt="hamburger style menu icon - open state"
|
||||
width=48
|
||||
height=48>
|
||||
</button>
|
||||
|
||||
<button id="header-close-menu-button" class="hide-content">
|
||||
<img
|
||||
src="../../png/hamburger-icon-closed.png"
|
||||
alt="hamburger style menu icon - closed state"
|
||||
width=48
|
||||
height=48>
|
||||
</button>
|
||||
|
||||
<div id="mobile-menu-panel" class="hide-content">
|
||||
<div class="mobile-menu-container">
|
||||
|
||||
<div class="mobile-menu-wrapper">
|
||||
<div class="mobile-menu-item">
|
||||
Entries
|
||||
</div>
|
||||
<div class="mobile-submenu-wrapper">
|
||||
|
||||
<a class="mobile-submenu-item" href="../../years.html">
|
||||
Winning entries
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../authors.html">
|
||||
Winning authors
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../location.html">
|
||||
Location of authors
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../bugs.html">
|
||||
Bugs and (mis)features
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html#fix_an_entry">
|
||||
Fixing entries
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html#fix_author">
|
||||
Updating author info
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../thanks-for-help.html">
|
||||
Thanks for the help
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mobile-menu-wrapper">
|
||||
|
||||
<div class="mobile-menu-item">
|
||||
Status
|
||||
</div>
|
||||
<div class="mobile-submenu-wrapper">
|
||||
|
||||
<a class="mobile-submenu-item" href="../../news.html">
|
||||
News
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../status.html">
|
||||
Contest status
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../next/rules.html">
|
||||
IOCCC rules
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../next/guidelines.html">
|
||||
IOCCC guidelines
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../markdown.html">
|
||||
Markdown guidelines
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../SECURITY.html">
|
||||
Security policy
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mobile-menu-wrapper">
|
||||
<div class="mobile-menu-item">
|
||||
FAQ
|
||||
</div>
|
||||
<div class="mobile-submenu-wrapper">
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html">
|
||||
Frequently asked questions
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../quick-start.html#enter">
|
||||
Enter the IOCCC
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html#compiling">
|
||||
Compiling entries
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html#running_entries">
|
||||
Running entries
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../faq.html#help">
|
||||
How to help
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mobile-menu-wrapper">
|
||||
<div class="mobile-menu-item">
|
||||
About
|
||||
</div>
|
||||
<div class="mobile-submenu-wrapper">
|
||||
|
||||
<a class="mobile-submenu-item" href="../../index.html">
|
||||
Home page
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../social.html">
|
||||
IOCCC social media
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../about.html">
|
||||
About the IOCCC
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../judges.html">
|
||||
The judges
|
||||
</a>
|
||||
|
||||
<a class="mobile-submenu-item" href="../../contact.html">
|
||||
Contact us
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var headerOpenMenuButton = document.getElementById("header-open-menu-button");
|
||||
var headerCloseMenuButton = document.getElementById("header-close-menu-button");
|
||||
var mobileMenuPanel = document.getElementById("mobile-menu-panel");
|
||||
|
||||
headerOpenMenuButton.addEventListener("click", () => {
|
||||
headerOpenMenuButton.classList.remove("topbar-mobile-menu");
|
||||
headerOpenMenuButton.classList.add("hide-content");
|
||||
|
||||
headerCloseMenuButton.classList.remove("hide-content");
|
||||
headerCloseMenuButton.classList.add("topbar-mobile-menu");
|
||||
|
||||
mobileMenuPanel.classList.remove("hide-content");
|
||||
mobileMenuPanel.classList.add("topbar-mobile-panel");
|
||||
});
|
||||
|
||||
headerCloseMenuButton.addEventListener("click", () => {
|
||||
headerCloseMenuButton.classList.remove("topbar-mobile-menu");
|
||||
headerCloseMenuButton.classList.add("hide-content");
|
||||
|
||||
mobileMenuPanel.classList.add("hide-content");
|
||||
mobileMenuPanel.classList.remove("topbar-mobile-panel");
|
||||
|
||||
headerOpenMenuButton.classList.add("topbar-mobile-menu");
|
||||
headerOpenMenuButton.classList.remove("hide-content");
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- END: this line ends content from: inc/topbar.default.html -->
|
||||
<!-- START: this line starts content from: inc/header.default.html -->
|
||||
|
||||
<div class="header">
|
||||
<a href="../../2011/zucker/index.html">
|
||||
<img src="../../png/ioccc.png"
|
||||
alt="IOCCC image by Matt Zucker"
|
||||
width=300
|
||||
height=110>
|
||||
</a>
|
||||
<h1>The International Obfuscated C Code Contest</h1>
|
||||
<h2>extra.html for 2024/burton</h2>
|
||||
</div>
|
||||
|
||||
<!-- END: this line ends content from: inc/header.default.html -->
|
||||
<!-- START: this line starts content from: inc/navbar.up2index.html -->
|
||||
|
||||
<div class="navbar">
|
||||
<a class="Left" href="index.html">↑ jump to 2024/burton/index.html ↑</a>
|
||||
<a class="Right" href="https://validator.w3.org/nu/?doc=https%3A%2F%2Fwww.ioccc.org%2F2024%2Fburton%2Fextra.html">✓</a>
|
||||
</div>
|
||||
|
||||
<!-- END: this line ends content from: inc/navbar.up2index.html -->
|
||||
<!-- START: this line starts content from: inc/before-content.default.html -->
|
||||
|
||||
<div class="content" id="content">
|
||||
|
||||
<!-- END: this line ends content from: inc/before-content.default.html -->
|
||||
<!-- START: this line starts content for HTML phase 21 by: bin/pandoc-wrapper.sh via bin/md2html.sh -->
|
||||
|
||||
<!-- BEFORE: 1st line of markdown file: 2024/burton/extra.md -->
|
||||
<h1 id="extra">extra</h1>
|
||||
<p>This readme provides a description of the extra files provided, how to use them,
|
||||
analysis of their results, and a deeper investigation on the failed guesses.</p>
|
||||
<p>The recommendation is to do:</p>
|
||||
<pre><code> make tools
|
||||
|
||||
./run dict blast swept # exhaust runs this command a LOT!
|
||||
./run dict crate swept # show the diff between starting words
|
||||
|
||||
mkdir results # create the results directory
|
||||
./exhaust # exhaustive test of all words (~1 hour)
|
||||
echo terns | ./word.result # add the word "terns" to results
|
||||
./mkresults > results.avg # proc exhaust into a single, small file</code></pre>
|
||||
<p>The files created as output from <code>exhaust</code> allow one to view, for each
|
||||
starting word, how each of the solutions resolve, including the misses.
|
||||
<code>mkresults</code> outputs a 1566 line report showing the starting word, its
|
||||
average accuracy, and the number of missed words. To show the number
|
||||
of guesses and the actual missed words, run <code>rank</code> on the desired word:</p>
|
||||
<pre><code> ./rank results/blast
|
||||
|
||||
1 1 # blast guessed itself
|
||||
2 112 # 112 words guessed in 2 attempts
|
||||
3 867 # 867 words guessed in 3 attempts
|
||||
4 1019 # ...
|
||||
5 271
|
||||
6 41
|
||||
foyer jaunt shave wound
|
||||
|
||||
./avg results/blast
|
||||
|
||||
blast 3.673002 4
|
||||
|
||||
grep blast results.avg
|
||||
|
||||
blast 3.673002 4</code></pre>
|
||||
<p>The C programs allow for (automated) testing:</p>
|
||||
<pre><code> gen.c - simulate wordle game by generating targets and scoring guesses
|
||||
./gen dict [target]
|
||||
|
||||
dups.c - remove words with(out) repeated letters
|
||||
(best starting words have no repeats)
|
||||
cat dict | wc -l # all the words
|
||||
cat dict | ./dups | wc -l # words with repeated letters
|
||||
cat dict | ./dups -n | wc -l # words without repeated letters
|
||||
|
||||
run.c - `gen` and `prog` connected via pipes; allows automated testing
|
||||
./run dict [start [target]]
|
||||
start - the first guess
|
||||
target - force the target to be the named word</code></pre>
|
||||
<h2 id="using-more-guesses">Using More Guesses</h2>
|
||||
<p>If you wish to see the failed words in action, and if more guesses would
|
||||
work, try:</p>
|
||||
<pre><code> ./run dict blast jaunt | grep ^gen
|
||||
|
||||
gen: target: jaunt
|
||||
gen: blast bbybg
|
||||
gen: daunt bgggg
|
||||
gen: haunt bgggg
|
||||
gen: gaunt bgggg
|
||||
gen: vaunt bgggg
|
||||
gen: taunt ygggg
|
||||
gen result end=0: n 6 lose: jaunt
|
||||
|
||||
GUESSES=7 ./run dict blast jaunt | grep ^gen
|
||||
|
||||
gen: target: jaunt
|
||||
gen: blast bbybg
|
||||
gen: daunt bgggg
|
||||
gen: haunt bgggg
|
||||
gen: gaunt bgggg
|
||||
gen: vaunt bgggg
|
||||
gen: taunt ygggg
|
||||
gen: jaunt ggggg
|
||||
gen result end=2: y 7 good: jaunt</code></pre>
|
||||
<p>Using <code>GUESSES=9</code> and <code>exhaust</code>, one gets a metric that scores words without
|
||||
misses, allowing a better comparison on the average score. Note, however,
|
||||
that the game doesn’t bend this way, and anything more than 6 guesses is a
|
||||
loss. Take this metric with a grain of salt. Also, 9 is the limit of
|
||||
guesses; the program will not work properly - if at all - with more than
|
||||
9 guesses.</p>
|
||||
<p>Ranking by fewest missed with GUESSES=6, the order is:</p>
|
||||
<pre><code> blast palsy climb flash lapse</code></pre>
|
||||
<p>Ranking by average with GUESSES=6, the order is:</p>
|
||||
<pre><code> crate slate trace crane least</code></pre>
|
||||
<p>Ranking by average with GUESSES=9, the order is:</p>
|
||||
<pre><code> slate trace crate train least</code></pre>
|
||||
<p>To reproduce these results:</p>
|
||||
<pre><code> GUESSES=9 ./exhaust results.9
|
||||
./mkresults results.9 > results.9.avg</code></pre>
|
||||
<h2 id="simple-analysis">Simple Analysis</h2>
|
||||
<p>The 1566 raw files produced by <code>exhaust</code> are ~370KB each (total ~562MB),
|
||||
far too large to submit, so included are the compressed results files for
|
||||
four starting words, and the summary file <code>results.avg</code>.</p>
|
||||
<pre><code> blast fewest missed
|
||||
crate best average
|
||||
query most missing
|
||||
terns example used in remarks</code></pre>
|
||||
<p>The program <code>rank</code> allows one to see how the averages are calculated, as well
|
||||
as the actual missed words. Comparing the results for <code>blast</code> and <code>terns</code>:</p>
|
||||
<pre><code> ./rank r.terns | awk 'NF==2' > a
|
||||
./rank r.blast | awk 'NF==2' > b
|
||||
join a b | column -t
|
||||
|
||||
guesses terns blast
|
||||
-------- ----- -----
|
||||
2 110 112
|
||||
3 909 867
|
||||
4 960 1019
|
||||
5 265 271
|
||||
6 53 41
|
||||
|
||||
F 18 4 (failed puzzles)</code></pre>
|
||||
<p>In general, the program <code>avg</code> is easier to use, but conveys less information:</p>
|
||||
<pre><code> ./avg r.terns; ./avg r.blast
|
||||
terns 3.641469 18
|
||||
blast 3.673002 4</code></pre>
|
||||
<p>You have to decide if you prefer a marginally lower average, but slightly
|
||||
higher chance of losing, versus a marginally higher average and lower chance
|
||||
of losing. This is developed further in the following section.</p>
|
||||
<p>It is interesting that the letter order makes a noticeable difference:</p>
|
||||
<pre><code> ./avg r.terns; ./avg results/stern
|
||||
terns 3.641469 18 # lower average and fewer misses
|
||||
stern 3.658315 20</code></pre>
|
||||
<p>For completeness, using <code>GUESSES=9</code> and the full starting dictionary from
|
||||
Wordle(tm), here are the results for the “best” starting words: <code>salet</code>,
|
||||
<code>slate</code>, and <code>blast</code>.</p>
|
||||
<pre><code> guesses salet slate blast
|
||||
------- ----- ----- -----
|
||||
1 0 1 1
|
||||
2 148 146 112
|
||||
3 932 939 867
|
||||
4 925 919 1019
|
||||
5 240 235 271
|
||||
6 56 57 41
|
||||
--- ----- ----- -----
|
||||
7 11 13 4
|
||||
8 2 4 0
|
||||
9 1 1 0
|
||||
--- ----- ----- -----
|
||||
avg 3.641 3.644 3.685</code></pre>
|
||||
<p>Obviously, <code>salet</code> produces the best results, excepting the losses (14).
|
||||
Just re-arranging the letters into <code>slate</code> produces almost the same,
|
||||
but there are more losses (18). If attempting to minimize losses,
|
||||
then <code>blast</code> is the optimal starting guess, since it never takes more
|
||||
than 7 guesses and has the fewest losses (4) of any starting word in the
|
||||
combined full Wordle(tm) dictionary.</p>
|
||||
<p>NB: The averages for <code>GUESSES=9</code> are larger than the default,
|
||||
because the misses are essentially folded into the average.
|
||||
The averages are more accurately compared when using the higher limit,
|
||||
although minimizing losses remains the priority (who likes to lose?).</p>
|
||||
<h2 id="seek-these">Seek These</h2>
|
||||
<p>The starting words with the fewest misses with <code>dict</code>:</p>
|
||||
<pre><code> sort -k3n results.avg | sed 18q | awk '{print $1, $3}' | column -t | pr -t -3
|
||||
|
||||
blast 4 ralph 7 crawl 8
|
||||
palsy 6 ranch 7 crumb 8
|
||||
climb 7 shawl 7 grasp 8
|
||||
flash 7 tramp 7 lymph 8
|
||||
lapse 7 clash 8 spark 8
|
||||
prowl 7 clasp 8 swamp 8
|
||||
|
||||
The starting words with the best accuracy:
|
||||
|
||||
sort -k2n results.avg | sed 18q | pr -t -3
|
||||
|
||||
crate 3.586609 20 irate 3.611231 31 trice 3.617279 15
|
||||
slate 3.586609 18 slice 3.612095 22 cater 3.620302 26
|
||||
trace 3.588769 18 stale 3.612959 19 satin 3.621598 19
|
||||
crane 3.595248 26 train 3.613391 14 snail 3.621598 19
|
||||
least 3.599568 19 trade 3.613823 20 stare 3.621598 19
|
||||
plate 3.606479 22 leant 3.616415 18 dealt 3.622462 15
|
||||
|
||||
Note the word with fewest misses, `blast`, is relatively far
|
||||
down the list in accuracy at 178th best:
|
||||
|
||||
sort -k2n results.avg | pr -t -n | grep blast
|
||||
|
||||
178 blast 3.673002 4</code></pre>
|
||||
<p>Versus starting words with fewest misses:</p>
|
||||
<pre><code> egrep 'terns|lapse|blast|climb|crate' results.avg
|
||||
|
||||
blast 3.673002 4
|
||||
climb 3.772354 7
|
||||
crate 3.586609 20
|
||||
lapse 3.664795 7
|
||||
terns 3.641469 18
|
||||
|
||||
weighted avg
|
||||
# blast crate blast crate improvement
|
||||
- ----- ----- --------------- -----------
|
||||
1 1 1 1.0000 1.0000 0.0000
|
||||
2 112 147 1.9912 1.9932 -0.0020
|
||||
3 867 952 2.8837 2.8645 -0.0192
|
||||
4 1019 884 3.4527 3.3951 -0.0576
|
||||
5 271 250 3.6374 3.5775 -0.0599
|
||||
6 41 61 3.6794 3.6429 -0.0365
|
||||
|
||||
F 4 20 -16 +16 (0.26% blast vs 1.28% crate)</code></pre>
|
||||
<p>Notice the difference in the average and weighted average. The program
|
||||
<code>avg</code> simply takes the total guesses as a percentage of the total words.
|
||||
The program <code>wavg</code> takes each of the six scores and weights them by the
|
||||
score. I wrote <code>wavg</code> later to track my progress playing the online game,
|
||||
and it is a more accurate score IMHO, but both are useful for comparisons.</p>
|
||||
<pre><code> $ ./avg results/blast
|
||||
blast 3.673002 4
|
||||
$ ./rank results/blast
|
||||
1 1
|
||||
2 112
|
||||
3 867
|
||||
4 1019
|
||||
5 271
|
||||
6 41
|
||||
foyer jaunt shave wound
|
||||
$ ./wavg 1 112 867 1019 271 41
|
||||
3.6794</code></pre>
|
||||
<p>The program <code>wrank</code> (weighted rank) makes this latter calculation much easier.
|
||||
It takes the output from <code>rank</code> and invokes <code>wavg</code> with these numbers,
|
||||
saving a lot of typing and errors.</p>
|
||||
<pre><code> ./wrank blast
|
||||
blast 3.6794</code></pre>
|
||||
<p>In words, <code>crate</code> as a first guess gets more correct answers in 2 and 3
|
||||
guesses, reducing its cost. It looks to be a stronger candidate, but it
|
||||
misses 16 more words (therefore losing the game more often). Your task
|
||||
is to determine your risk profile, and decide among the variety of possible
|
||||
starting points, then let <code>prog</code> recommend your next guess.</p>
|
||||
<p>A somewhat laborious calculation provides the answer to which starting word
|
||||
has the most answers in two words and three words:</p>
|
||||
<pre><code> # two words
|
||||
for n in results/*; do
|
||||
rank $n | awk -v word=$n '/^2/ {print word,$2}';
|
||||
done | sort -k2nr | sed 3q
|
||||
|
||||
trace 149
|
||||
crate 147
|
||||
slate 146
|
||||
|
||||
# three words
|
||||
for n in results/*; do
|
||||
rank $n | awk -v word=$n '/^3/ {print word,$2}';
|
||||
done | sort -k2nr | sed 4q
|
||||
|
||||
crate 952
|
||||
train 944
|
||||
trace 939
|
||||
slate 936
|
||||
|
||||
# versus the averages
|
||||
sort -k2n -k3n results.avg | sed 3q
|
||||
slate 3.586609 18
|
||||
crate 3.586609 20
|
||||
trace 3.588769 18</code></pre>
|
||||
<h2 id="avoid-these">Avoid These</h2>
|
||||
<p>The starting words with the worst accuracy:</p>
|
||||
<pre><code> sort -k2nr results.avg | sed 18q | pr -t -3
|
||||
|
||||
jumpy 3.997408 21 juicy 3.954212 39 squib 3.927862 28
|
||||
jumbo 3.996976 25 quake 3.952052 38 quark 3.926566 30
|
||||
buxom 3.995248 23 jerky 3.950756 31 quoth 3.920950 33
|
||||
quick 3.987041 36 equip 3.939957 22 gawky 3.918790 22
|
||||
quack 3.974514 23 quasi 3.938661 32 unzip 3.915335 24
|
||||
quirk 3.964147 40 epoxy 3.934773 27 judge 3.914903 33</code></pre>
|
||||
<p>The starting words with the most failures (missed words):</p>
|
||||
<pre><code> sort -k3nr results.avg | sed 18q | pr -t -3
|
||||
|
||||
query 3.893305 43 juicy 3.954212 39 enjoy 3.890713 37
|
||||
quite 3.835421 42 quota 3.881641 39 gauze 3.878186 37
|
||||
yield 3.740821 41 quake 3.952052 38 oxide 3.888121 37
|
||||
juice 3.847084 40 quiet 3.857451 38 fiery 3.772786 36
|
||||
quirk 3.964147 40 quote 3.829374 38 quail 3.893737 36
|
||||
axion 3.796544 39 unify 3.862203 38 quick 3.987041 36</code></pre>
|
||||
<p>The worst target words, for any starting word (N out of 1566):</p>
|
||||
<pre><code> ./worst
|
||||
|
||||
wound 1340 jaunt 1254 hatch 1157 foyer 1109 waste 1057
|
||||
found 997 shave 945 wight 943 willy 790 catch 779
|
||||
boxer 762 jolly 751 graze 626 rower 611 fatty 598
|
||||
roger 568 taunt 520 watch 497 witty 483 wooer 451
|
||||
tight 449 bound 442 swore 415 taste 405 mower 378
|
||||
hover 374 bunch 371 shake 358 racer 348 rarer 294
|
||||
paper 289 water 285 stave 271 ferry 250 fever 242
|
||||
fully 241 paddy 239 fight 225 parer 219 taffy 207
|
||||
frown 196 joker 195 filly 188 batty 179 lowly 179
|
||||
staff 176 jazzy 174 piper 152 vying 152 swear 151</code></pre>
|
||||
<h2 id="spoiler-deep-dive-on-near-misses-spoiler">SPOILER Deep Dive on Near Misses SPOILER</h2>
|
||||
<p>Dissecting the failed targets when starting with <code>blast</code>. Recall <code>blast</code>
|
||||
only misses 4 words out of all 2315:</p>
|
||||
<pre><code> foyer jaunt shave wound</code></pre>
|
||||
<p>These four words have common sub-patterns, which in hard-mode is difficult
|
||||
to disambiguate. Using <code>run dict blast $word</code>, here are the grep sub-patterns
|
||||
and counts for these four words as revealed in the output:</p>
|
||||
<pre><code> foyer 25 grep '^.o.er' dict | wc -l
|
||||
jaunt 6 grep '^.aunt' dict | wc -l
|
||||
shave 7 grep '^sha.e' dict | wc -l
|
||||
wound 8 grep '^.ound' dict | wc -l</code></pre>
|
||||
<p>Looking at the patterns, the program is making guesses that just happen to
|
||||
select the correct word last in its choices. In all cases, on the seventh
|
||||
guess, the program has the correct answer using <code>blast</code> as the starting guess.
|
||||
It is easy to do an exhaustive look for only four words, but as the count
|
||||
goes up, so does the work.</p>
|
||||
<p>Several variations were attempted, including randomization of the few results
|
||||
remaining. Besides being non-deterministic (this time it wins, next time it
|
||||
loses), better results were obtained with other changes to the selection
|
||||
algorithm. Some small insight into the algorithm used may be seen by the
|
||||
<code>grep</code> command lines, although there are additional insights used within
|
||||
the program that improve significantly upon these results.</p>
|
||||
<pre><code> ./run dict blast foyer | grep ^gen
|
||||
gen: target: foyer 2315
|
||||
gen: blast bbbbb 429 grep -v '[blast]' dict | wc -l
|
||||
gen: drone byyby 27 grep r dict | grep o | grep e | grep -v '[blastdn]' | wc -l
|
||||
gen: cower bgbgg 8 grep '^.o.er' dict | grep -v '[blastdncw]'
|
||||
foyer homer hover joker
|
||||
mover poker roger rover
|
||||
gen: mover bgbgg 4 grep '.o.er' dict | grep -v '[blastdncwmv]'
|
||||
foyer joker poker roger
|
||||
gen: poker bgbgg rm poker joker (foyer roger)
|
||||
gen: roger ygbgg rm roger (foyer)
|
||||
gen result end=0: n 6 lose: foyer
|
||||
|
||||
---------------
|
||||
|
||||
./run dict blast jaunt | grep ^gen
|
||||
gen: target: jaunt 2315
|
||||
gen: blast bbybg 46 grep 't$' dict | grep a | grep -v '[bls]' | wc -l
|
||||
gen: daunt bgggg 5 grep '^.aunt$' dict | grep -v '[blsd]'
|
||||
gaunt haunt jaunt taunt
|
||||
vaunt
|
||||
gen: haunt bgggg rm haunt (gaunt, jaunt, taunt, vaunt)
|
||||
gen: gaunt bgggg rm guant (jaunt, taunt, vaunt)
|
||||
gen: vaunt bgggg rm vaunt (jaunt, taunt)
|
||||
gen: taunt ygggg rm taunt (jaunt)
|
||||
gen result end=0: n 6 lose: jaunt
|
||||
|
||||
---------------
|
||||
|
||||
./run dict blast shave | grep ^gen
|
||||
gen: target: shake 2315
|
||||
gen: blast bbgyb 52 grep '^..a' dict | grep s | grep -v '[blt]' | wc -l
|
||||
gen: share gggbg 5 grep '^sha.e' dict | grep -v '[bltr]'
|
||||
shade shake shame shape
|
||||
shave
|
||||
gen: shade gggbg rm shade (shake shame shape shave)
|
||||
gen: shape gggbg rm shape (shake shame shave)
|
||||
gen: shame gggbg rm shame (shake shave)
|
||||
gen: shake gggbg rm shake (shave)
|
||||
gen result end=0: n 6 lose: shave
|
||||
|
||||
---------------
|
||||
|
||||
./run dict blast wound | grep ^gen
|
||||
gen: target: wound 2315
|
||||
gen: blast bbbbb 429 grep -v '[blast]' dict | wc -l
|
||||
gen: drone ybygb 7 grep 'n.$' dict | grep d | grep o | grep -v '[blastre]' | wc -l
|
||||
gen: pound bgggg 4 grep '^.ound' dict | grep -v '[blastreh]'
|
||||
found hound mound wound
|
||||
gen: mound bgggg rm mound (found, hound, wound)
|
||||
gen: hound bgggg rm hound (found, wound)
|
||||
gen: found bgggg rm found (wound)
|
||||
gen result end=0: n 6 lose: wound</code></pre>
|
||||
<!-- AFTER: last line of markdown file: 2024/burton/extra.md -->
|
||||
|
||||
<!-- END: this line ends content for HTML phase 21 by: bin/pandoc-wrapper.sh via bin/md2html.sh -->
|
||||
<!-- START: this line starts content from: inc/after-content.default.html -->
|
||||
|
||||
</div>
|
||||
|
||||
<!-- END: this line ends content from: inc/after-content.default.html -->
|
||||
<!-- START: this line starts content from: inc/footer.default.html -->
|
||||
|
||||
<!--
|
||||
|
||||
Copyright © 1984-2025 by Landon Curt Noll. All Rights Reserved.
|
||||
|
||||
You are free to share and adapt this file under the terms of this license:
|
||||
|
||||
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
|
||||
|
||||
For more information, see:
|
||||
|
||||
https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
-->
|
||||
|
||||
<div class="footer">
|
||||
|
||||
<div id="copyright"><h3>Copyright © 1984-2025 by Landon Curt Noll:
|
||||
<a href="https://creativecommons.org/faq/#what-does-some-rights-reserved-mean"
|
||||
target="_blank"
|
||||
rel="license noopener noreferrer">Some Rights Reserved</a></h3>
|
||||
<p>
|
||||
This work is <b>licensed by Landon Curt Noll</b> under
|
||||
<b><a href="https://creativecommons.org/licenses/by-sa/4.0/?ref=chooser-v1"
|
||||
target="_blank"
|
||||
rel="license noopener noreferrer"
|
||||
style="display:inline-block;">CC BY-SA 4.0</a></b>.
|
||||
<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;"
|
||||
src="../../png/cc.png"
|
||||
alt="cc inside circle symbol">
|
||||
<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;"
|
||||
src="../../png/by.png"
|
||||
alt="person inside circle symbol">
|
||||
<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;"
|
||||
src="../../png/sa.png"
|
||||
alt="arrow looping back on itself inside circle symbol"><br>
|
||||
You should <b>carefully review</b> the
|
||||
<b><a href="https://creativecommons.org/licenses/by-sa/4.0/legalcode.en"
|
||||
target="_blank"
|
||||
rel="license noopener noreferrer">CC BY-SA 4.0 LEGAL CODE</a></b>
|
||||
before using the licensed material.<br>
|
||||
You may wish to review the
|
||||
<b><a href="../../license.html"
|
||||
target="_blank"
|
||||
rel="license noopener noreferrer">highlights of some of the key features and terms</a></b>
|
||||
of <b>CC BY-SA 4.0</b>.<br>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="coda"><h3>Coda</h3>
|
||||
<p>
|
||||
<a href="https://validator.w3.org/nu/?doc=https%3A%2F%2Fwww.ioccc.org%2F2024%2Fburton%2Fextra.html" rel="nofollow">Nu HTML check this web page</a><br>
|
||||
<a href="#top">Jump to top</a> <a href="#content">Jump to Content</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- END: this line ends content from: inc/footer.default.html -->
|
||||
<!-- START: this line starts content from: inc/bottom.default.html -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<!-- END: this line ends content from: inc/bottom.default.html -->
|
||||
@@ -0,0 +1,471 @@
|
||||
# extra
|
||||
|
||||
This readme provides a description of the extra files provided, how to use them,
|
||||
analysis of their results, and a deeper investigation on the failed guesses.
|
||||
|
||||
The recommendation is to do:
|
||||
|
||||
``` <!---sh-->
|
||||
make tools
|
||||
|
||||
./run dict blast swept # exhaust runs this command a LOT!
|
||||
./run dict crate swept # show the diff between starting words
|
||||
|
||||
mkdir results # create the results directory
|
||||
./exhaust # exhaustive test of all words (~1 hour)
|
||||
echo terns | ./word.result # add the word "terns" to results
|
||||
./mkresults > results.avg # proc exhaust into a single, small file
|
||||
```
|
||||
|
||||
The files created as output from `exhaust` allow one to view, for each
|
||||
starting word, how each of the solutions resolve, including the misses.
|
||||
`mkresults` outputs a 1566 line report showing the starting word, its
|
||||
average accuracy, and the number of missed words. To show the number
|
||||
of guesses and the actual missed words, run `rank` on the desired word:
|
||||
|
||||
``` <!---sh-->
|
||||
./rank results/blast
|
||||
|
||||
1 1 # blast guessed itself
|
||||
2 112 # 112 words guessed in 2 attempts
|
||||
3 867 # 867 words guessed in 3 attempts
|
||||
4 1019 # ...
|
||||
5 271
|
||||
6 41
|
||||
foyer jaunt shave wound
|
||||
|
||||
./avg results/blast
|
||||
|
||||
blast 3.673002 4
|
||||
|
||||
grep blast results.avg
|
||||
|
||||
blast 3.673002 4
|
||||
```
|
||||
|
||||
The C programs allow for (automated) testing:
|
||||
|
||||
```
|
||||
gen.c - simulate wordle game by generating targets and scoring guesses
|
||||
./gen dict [target]
|
||||
|
||||
dups.c - remove words with(out) repeated letters
|
||||
(best starting words have no repeats)
|
||||
cat dict | wc -l # all the words
|
||||
cat dict | ./dups | wc -l # words with repeated letters
|
||||
cat dict | ./dups -n | wc -l # words without repeated letters
|
||||
|
||||
run.c - `gen` and `prog` connected via pipes; allows automated testing
|
||||
./run dict [start [target]]
|
||||
start - the first guess
|
||||
target - force the target to be the named word
|
||||
```
|
||||
|
||||
|
||||
## Using More Guesses
|
||||
|
||||
If you wish to see the failed words in action, and if more guesses would
|
||||
work, try:
|
||||
|
||||
```
|
||||
./run dict blast jaunt | grep ^gen
|
||||
|
||||
gen: target: jaunt
|
||||
gen: blast bbybg
|
||||
gen: daunt bgggg
|
||||
gen: haunt bgggg
|
||||
gen: gaunt bgggg
|
||||
gen: vaunt bgggg
|
||||
gen: taunt ygggg
|
||||
gen result end=0: n 6 lose: jaunt
|
||||
|
||||
GUESSES=7 ./run dict blast jaunt | grep ^gen
|
||||
|
||||
gen: target: jaunt
|
||||
gen: blast bbybg
|
||||
gen: daunt bgggg
|
||||
gen: haunt bgggg
|
||||
gen: gaunt bgggg
|
||||
gen: vaunt bgggg
|
||||
gen: taunt ygggg
|
||||
gen: jaunt ggggg
|
||||
gen result end=2: y 7 good: jaunt
|
||||
```
|
||||
|
||||
Using `GUESSES=9` and `exhaust`, one gets a metric that scores words without
|
||||
misses, allowing a better comparison on the average score. Note, however,
|
||||
that the game doesn't bend this way, and anything more than 6 guesses is a
|
||||
loss. Take this metric with a grain of salt. Also, 9 is the limit of
|
||||
guesses; the program will not work properly - if at all - with more than
|
||||
9 guesses.
|
||||
|
||||
Ranking by fewest missed with GUESSES=6, the order is:
|
||||
|
||||
```
|
||||
blast palsy climb flash lapse
|
||||
```
|
||||
|
||||
Ranking by average with GUESSES=6, the order is:
|
||||
|
||||
```
|
||||
crate slate trace crane least
|
||||
```
|
||||
|
||||
Ranking by average with GUESSES=9, the order is:
|
||||
|
||||
```
|
||||
slate trace crate train least
|
||||
```
|
||||
|
||||
To reproduce these results:
|
||||
|
||||
```
|
||||
GUESSES=9 ./exhaust results.9
|
||||
./mkresults results.9 > results.9.avg
|
||||
```
|
||||
|
||||
|
||||
## Simple Analysis
|
||||
|
||||
The 1566 raw files produced by `exhaust` are ~370KB each (total ~562MB),
|
||||
far too large to submit, so included are the compressed results files for
|
||||
four starting words, and the summary file `results.avg`.
|
||||
|
||||
```
|
||||
blast fewest missed
|
||||
crate best average
|
||||
query most missing
|
||||
terns example used in remarks
|
||||
```
|
||||
|
||||
The program `rank` allows one to see how the averages are calculated, as well
|
||||
as the actual missed words. Comparing the results for `blast` and `terns`:
|
||||
|
||||
```
|
||||
./rank r.terns | awk 'NF==2' > a
|
||||
./rank r.blast | awk 'NF==2' > b
|
||||
join a b | column -t
|
||||
|
||||
guesses terns blast
|
||||
-------- ----- -----
|
||||
2 110 112
|
||||
3 909 867
|
||||
4 960 1019
|
||||
5 265 271
|
||||
6 53 41
|
||||
|
||||
F 18 4 (failed puzzles)
|
||||
```
|
||||
|
||||
In general, the program `avg` is easier to use, but conveys less information:
|
||||
|
||||
```
|
||||
./avg r.terns; ./avg r.blast
|
||||
terns 3.641469 18
|
||||
blast 3.673002 4
|
||||
```
|
||||
|
||||
You have to decide if you prefer a marginally lower average, but slightly
|
||||
higher chance of losing, versus a marginally higher average and lower chance
|
||||
of losing. This is developed further in the following section.
|
||||
|
||||
It is interesting that the letter order makes a noticeable difference:
|
||||
|
||||
```
|
||||
./avg r.terns; ./avg results/stern
|
||||
terns 3.641469 18 # lower average and fewer misses
|
||||
stern 3.658315 20
|
||||
```
|
||||
|
||||
For completeness, using `GUESSES=9` and the full starting dictionary from
|
||||
Wordle(tm), here are the results for the "best" starting words: `salet`,
|
||||
`slate`, and `blast`.
|
||||
|
||||
```
|
||||
guesses salet slate blast
|
||||
------- ----- ----- -----
|
||||
1 0 1 1
|
||||
2 148 146 112
|
||||
3 932 939 867
|
||||
4 925 919 1019
|
||||
5 240 235 271
|
||||
6 56 57 41
|
||||
--- ----- ----- -----
|
||||
7 11 13 4
|
||||
8 2 4 0
|
||||
9 1 1 0
|
||||
--- ----- ----- -----
|
||||
avg 3.641 3.644 3.685
|
||||
```
|
||||
|
||||
Obviously, `salet` produces the best results, excepting the losses (14).
|
||||
Just re-arranging the letters into `slate` produces almost the same,
|
||||
but there are more losses (18). If attempting to minimize losses,
|
||||
then `blast` is the optimal starting guess, since it never takes more
|
||||
than 7 guesses and has the fewest losses (4) of any starting word in the
|
||||
combined full Wordle(tm) dictionary.
|
||||
|
||||
NB: The averages for `GUESSES=9` are larger than the default,
|
||||
because the misses are essentially folded into the average.
|
||||
The averages are more accurately compared when using the higher limit,
|
||||
although minimizing losses remains the priority (who likes to lose?).
|
||||
|
||||
|
||||
## Seek These
|
||||
|
||||
The starting words with the fewest misses with `dict`:
|
||||
|
||||
```
|
||||
sort -k3n results.avg | sed 18q | awk '{print $1, $3}' | column -t | pr -t -3
|
||||
|
||||
blast 4 ralph 7 crawl 8
|
||||
palsy 6 ranch 7 crumb 8
|
||||
climb 7 shawl 7 grasp 8
|
||||
flash 7 tramp 7 lymph 8
|
||||
lapse 7 clash 8 spark 8
|
||||
prowl 7 clasp 8 swamp 8
|
||||
|
||||
The starting words with the best accuracy:
|
||||
|
||||
sort -k2n results.avg | sed 18q | pr -t -3
|
||||
|
||||
crate 3.586609 20 irate 3.611231 31 trice 3.617279 15
|
||||
slate 3.586609 18 slice 3.612095 22 cater 3.620302 26
|
||||
trace 3.588769 18 stale 3.612959 19 satin 3.621598 19
|
||||
crane 3.595248 26 train 3.613391 14 snail 3.621598 19
|
||||
least 3.599568 19 trade 3.613823 20 stare 3.621598 19
|
||||
plate 3.606479 22 leant 3.616415 18 dealt 3.622462 15
|
||||
|
||||
Note the word with fewest misses, `blast`, is relatively far
|
||||
down the list in accuracy at 178th best:
|
||||
|
||||
sort -k2n results.avg | pr -t -n | grep blast
|
||||
|
||||
178 blast 3.673002 4
|
||||
```
|
||||
|
||||
Versus starting words with fewest misses:
|
||||
|
||||
```
|
||||
egrep 'terns|lapse|blast|climb|crate' results.avg
|
||||
|
||||
blast 3.673002 4
|
||||
climb 3.772354 7
|
||||
crate 3.586609 20
|
||||
lapse 3.664795 7
|
||||
terns 3.641469 18
|
||||
|
||||
weighted avg
|
||||
# blast crate blast crate improvement
|
||||
- ----- ----- --------------- -----------
|
||||
1 1 1 1.0000 1.0000 0.0000
|
||||
2 112 147 1.9912 1.9932 -0.0020
|
||||
3 867 952 2.8837 2.8645 -0.0192
|
||||
4 1019 884 3.4527 3.3951 -0.0576
|
||||
5 271 250 3.6374 3.5775 -0.0599
|
||||
6 41 61 3.6794 3.6429 -0.0365
|
||||
|
||||
F 4 20 -16 +16 (0.26% blast vs 1.28% crate)
|
||||
```
|
||||
|
||||
Notice the difference in the average and weighted average. The program
|
||||
`avg` simply takes the total guesses as a percentage of the total words.
|
||||
The program `wavg` takes each of the six scores and weights them by the
|
||||
score. I wrote `wavg` later to track my progress playing the online game,
|
||||
and it is a more accurate score IMHO, but both are useful for comparisons.
|
||||
|
||||
``` <!---sh-->
|
||||
$ ./avg results/blast
|
||||
blast 3.673002 4
|
||||
$ ./rank results/blast
|
||||
1 1
|
||||
2 112
|
||||
3 867
|
||||
4 1019
|
||||
5 271
|
||||
6 41
|
||||
foyer jaunt shave wound
|
||||
$ ./wavg 1 112 867 1019 271 41
|
||||
3.6794
|
||||
```
|
||||
|
||||
The program `wrank` (weighted rank) makes this latter calculation much easier.
|
||||
It takes the output from `rank` and invokes `wavg` with these numbers,
|
||||
saving a lot of typing and errors.
|
||||
|
||||
``` <!---sh-->
|
||||
./wrank blast
|
||||
blast 3.6794
|
||||
```
|
||||
|
||||
In words, `crate` as a first guess gets more correct answers in 2 and 3
|
||||
guesses, reducing its cost. It looks to be a stronger candidate, but it
|
||||
misses 16 more words (therefore losing the game more often). Your task
|
||||
is to determine your risk profile, and decide among the variety of possible
|
||||
starting points, then let `prog` recommend your next guess.
|
||||
|
||||
A somewhat laborious calculation provides the answer to which starting word
|
||||
has the most answers in two words and three words:
|
||||
|
||||
```
|
||||
# two words
|
||||
for n in results/*; do
|
||||
rank $n | awk -v word=$n '/^2/ {print word,$2}';
|
||||
done | sort -k2nr | sed 3q
|
||||
|
||||
trace 149
|
||||
crate 147
|
||||
slate 146
|
||||
|
||||
# three words
|
||||
for n in results/*; do
|
||||
rank $n | awk -v word=$n '/^3/ {print word,$2}';
|
||||
done | sort -k2nr | sed 4q
|
||||
|
||||
crate 952
|
||||
train 944
|
||||
trace 939
|
||||
slate 936
|
||||
|
||||
# versus the averages
|
||||
sort -k2n -k3n results.avg | sed 3q
|
||||
slate 3.586609 18
|
||||
crate 3.586609 20
|
||||
trace 3.588769 18
|
||||
```
|
||||
|
||||
|
||||
## Avoid These
|
||||
|
||||
The starting words with the worst accuracy:
|
||||
|
||||
```
|
||||
sort -k2nr results.avg | sed 18q | pr -t -3
|
||||
|
||||
jumpy 3.997408 21 juicy 3.954212 39 squib 3.927862 28
|
||||
jumbo 3.996976 25 quake 3.952052 38 quark 3.926566 30
|
||||
buxom 3.995248 23 jerky 3.950756 31 quoth 3.920950 33
|
||||
quick 3.987041 36 equip 3.939957 22 gawky 3.918790 22
|
||||
quack 3.974514 23 quasi 3.938661 32 unzip 3.915335 24
|
||||
quirk 3.964147 40 epoxy 3.934773 27 judge 3.914903 33
|
||||
```
|
||||
|
||||
The starting words with the most failures (missed words):
|
||||
|
||||
```
|
||||
sort -k3nr results.avg | sed 18q | pr -t -3
|
||||
|
||||
query 3.893305 43 juicy 3.954212 39 enjoy 3.890713 37
|
||||
quite 3.835421 42 quota 3.881641 39 gauze 3.878186 37
|
||||
yield 3.740821 41 quake 3.952052 38 oxide 3.888121 37
|
||||
juice 3.847084 40 quiet 3.857451 38 fiery 3.772786 36
|
||||
quirk 3.964147 40 quote 3.829374 38 quail 3.893737 36
|
||||
axion 3.796544 39 unify 3.862203 38 quick 3.987041 36
|
||||
```
|
||||
|
||||
The worst target words, for any starting word (N out of 1566):
|
||||
|
||||
```
|
||||
./worst
|
||||
|
||||
wound 1340 jaunt 1254 hatch 1157 foyer 1109 waste 1057
|
||||
found 997 shave 945 wight 943 willy 790 catch 779
|
||||
boxer 762 jolly 751 graze 626 rower 611 fatty 598
|
||||
roger 568 taunt 520 watch 497 witty 483 wooer 451
|
||||
tight 449 bound 442 swore 415 taste 405 mower 378
|
||||
hover 374 bunch 371 shake 358 racer 348 rarer 294
|
||||
paper 289 water 285 stave 271 ferry 250 fever 242
|
||||
fully 241 paddy 239 fight 225 parer 219 taffy 207
|
||||
frown 196 joker 195 filly 188 batty 179 lowly 179
|
||||
staff 176 jazzy 174 piper 152 vying 152 swear 151
|
||||
```
|
||||
|
||||
|
||||
## SPOILER Deep Dive on Near Misses SPOILER
|
||||
|
||||
Dissecting the failed targets when starting with `blast`. Recall `blast`
|
||||
only misses 4 words out of all 2315:
|
||||
|
||||
```
|
||||
foyer jaunt shave wound
|
||||
```
|
||||
|
||||
These four words have common sub-patterns, which in hard-mode is difficult
|
||||
to disambiguate. Using `run dict blast $word`, here are the grep sub-patterns
|
||||
and counts for these four words as revealed in the output:
|
||||
|
||||
```
|
||||
foyer 25 grep '^.o.er' dict | wc -l
|
||||
jaunt 6 grep '^.aunt' dict | wc -l
|
||||
shave 7 grep '^sha.e' dict | wc -l
|
||||
wound 8 grep '^.ound' dict | wc -l
|
||||
```
|
||||
|
||||
Looking at the patterns, the program is making guesses that just happen to
|
||||
select the correct word last in its choices. In all cases, on the seventh
|
||||
guess, the program has the correct answer using `blast` as the starting guess.
|
||||
It is easy to do an exhaustive look for only four words, but as the count
|
||||
goes up, so does the work.
|
||||
|
||||
Several variations were attempted, including randomization of the few results
|
||||
remaining. Besides being non-deterministic (this time it wins, next time it
|
||||
loses), better results were obtained with other changes to the selection
|
||||
algorithm. Some small insight into the algorithm used may be seen by the
|
||||
`grep` command lines, although there are additional insights used within
|
||||
the program that improve significantly upon these results.
|
||||
|
||||
```
|
||||
./run dict blast foyer | grep ^gen
|
||||
gen: target: foyer 2315
|
||||
gen: blast bbbbb 429 grep -v '[blast]' dict | wc -l
|
||||
gen: drone byyby 27 grep r dict | grep o | grep e | grep -v '[blastdn]' | wc -l
|
||||
gen: cower bgbgg 8 grep '^.o.er' dict | grep -v '[blastdncw]'
|
||||
foyer homer hover joker
|
||||
mover poker roger rover
|
||||
gen: mover bgbgg 4 grep '.o.er' dict | grep -v '[blastdncwmv]'
|
||||
foyer joker poker roger
|
||||
gen: poker bgbgg rm poker joker (foyer roger)
|
||||
gen: roger ygbgg rm roger (foyer)
|
||||
gen result end=0: n 6 lose: foyer
|
||||
|
||||
---------------
|
||||
|
||||
./run dict blast jaunt | grep ^gen
|
||||
gen: target: jaunt 2315
|
||||
gen: blast bbybg 46 grep 't$' dict | grep a | grep -v '[bls]' | wc -l
|
||||
gen: daunt bgggg 5 grep '^.aunt$' dict | grep -v '[blsd]'
|
||||
gaunt haunt jaunt taunt
|
||||
vaunt
|
||||
gen: haunt bgggg rm haunt (gaunt, jaunt, taunt, vaunt)
|
||||
gen: gaunt bgggg rm guant (jaunt, taunt, vaunt)
|
||||
gen: vaunt bgggg rm vaunt (jaunt, taunt)
|
||||
gen: taunt ygggg rm taunt (jaunt)
|
||||
gen result end=0: n 6 lose: jaunt
|
||||
|
||||
---------------
|
||||
|
||||
./run dict blast shave | grep ^gen
|
||||
gen: target: shake 2315
|
||||
gen: blast bbgyb 52 grep '^..a' dict | grep s | grep -v '[blt]' | wc -l
|
||||
gen: share gggbg 5 grep '^sha.e' dict | grep -v '[bltr]'
|
||||
shade shake shame shape
|
||||
shave
|
||||
gen: shade gggbg rm shade (shake shame shape shave)
|
||||
gen: shape gggbg rm shape (shake shame shave)
|
||||
gen: shame gggbg rm shame (shake shave)
|
||||
gen: shake gggbg rm shake (shave)
|
||||
gen result end=0: n 6 lose: shave
|
||||
|
||||
---------------
|
||||
|
||||
./run dict blast wound | grep ^gen
|
||||
gen: target: wound 2315
|
||||
gen: blast bbbbb 429 grep -v '[blast]' dict | wc -l
|
||||
gen: drone ybygb 7 grep 'n.$' dict | grep d | grep o | grep -v '[blastre]' | wc -l
|
||||
gen: pound bgggg 4 grep '^.ound' dict | grep -v '[blastreh]'
|
||||
found hound mound wound
|
||||
gen: mound bgggg rm mound (found, hound, wound)
|
||||
gen: hound bgggg rm hound (found, wound)
|
||||
gen: found bgggg rm found (wound)
|
||||
gen result end=0: n 6 lose: wound
|
||||
```
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
w=$1; : ${w:?echo "usage: $0 target_word" >&2; exit 1}
|
||||
|
||||
rank results/$w |
|
||||
sed -n 7,\$p |
|
||||
tr '\t' '\n' |
|
||||
awk '$1 != "" {print $1}' |
|
||||
sort -u |
|
||||
while read a; do
|
||||
GUESSES=9 run dict $w $a
|
||||
done |
|
||||
grep result
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
dict=$1
|
||||
: ${dict:=dict}
|
||||
|
||||
echo first
|
||||
seq a z | while read a; do /bin/echo -n "$a: "; grep ^$a $dict | wc -l; done | sort -k2nr
|
||||
echo second
|
||||
seq a z | while read a; do /bin/echo -n "$a: "; grep ^.$a $dict | wc -l; done | sort -k2nr
|
||||
echo third
|
||||
seq a z | while read a; do /bin/echo -n "$a: "; grep ^..$a $dict | wc -l; done | sort -k2nr
|
||||
echo fourth
|
||||
seq a z | while read a; do /bin/echo -n "$a: "; grep ^...$a $dict | wc -l; done | sort -k2nr
|
||||
echo last
|
||||
seq a z | while read a; do /bin/echo -n "$a: "; grep ^....$a $dict | wc -l; done | sort -k2nr
|
||||
@@ -0,0 +1,86 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* Wordle generator - text mode
|
||||
* This program works in coordination with the obfuscated code
|
||||
* to allow one to play more rapidly (e.g. test the algorithm)
|
||||
* or just to play along in text mode.
|
||||
*
|
||||
* This version is a pale imitation of the Real Wordle(tm).
|
||||
* If you want to get the real experience, please visit:
|
||||
*
|
||||
* https://www.nytimes.com/games/wordle/index.html
|
||||
*/
|
||||
|
||||
static int
|
||||
check(char *w, char *target)
|
||||
{
|
||||
printf("%.5s ", w);
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
if (w[i] == target[i])
|
||||
putchar('g');
|
||||
else if (memchr(target, w[i], 5))
|
||||
putchar('y');
|
||||
else
|
||||
putchar('b');
|
||||
}
|
||||
putchar('\n');
|
||||
fflush(stdout);
|
||||
return memcmp(w, target, 5) == 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int ac, char **av)
|
||||
{
|
||||
char words[78557][5];
|
||||
char buf[100];
|
||||
int cnt = 0, win = 0;
|
||||
int nguesses = 6;
|
||||
|
||||
if (ac < 2) {
|
||||
fprintf(stderr, "usage: %s dict [target]\n", av[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (getenv("GUESSES")) {
|
||||
nguesses = atoi(getenv("GUESSES"));
|
||||
// fprintf(stderr, "using %d guesses\n", nguesses);
|
||||
}
|
||||
|
||||
FILE *fp = fopen(av[1], "r");
|
||||
if (!fp) {
|
||||
perror(av[1]);
|
||||
exit(1);
|
||||
}
|
||||
while (fgets(buf, sizeof buf, fp)) {
|
||||
if (buf[5] != '\n')
|
||||
continue;
|
||||
memcpy(words[cnt++], buf, 5);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
srandom(time(0) ^ getpid());
|
||||
|
||||
char *target = ac == 3 ? av[2] : words[random() % cnt];
|
||||
getenv("CHEAT") && printf("target: %.5s\n", target);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < nguesses; ) {
|
||||
printf("? "); fflush(stdout);
|
||||
if (!fgets(buf, sizeof buf, stdin))
|
||||
break;
|
||||
if (buf[5] != '\n') {
|
||||
printf("you must enter 5 letters\n");
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
if ((win = check(buf, target)))
|
||||
break;
|
||||
}
|
||||
printf("%c %d %s: %.5s\n", win ? 'y' : 'n', i, win ? "good" : "lose", target);
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
dir=$1
|
||||
: ${dir:=results}
|
||||
|
||||
for name in $dir/*; do
|
||||
./avg $name
|
||||
done | sed "s#$dir/##" | tr : ' '
|
||||
@@ -0,0 +1,64 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
# include <stdio.h>
|
||||
#define J(J) for (\
|
||||
_ j=0; j<5; ++j) J;
|
||||
#define/*IOCCC*/I \
|
||||
\
|
||||
\
|
||||
\
|
||||
_ W,_ O,_ R,_ D,_*L,_*E
|
||||
|
||||
|
||||
#define A(A )for(_ j=0; !o&&j<5&&(k = t[l][j]); ++j)A&&++o;
|
||||
typedef/* ; ; */void q; typedef int _ ; typedef char/* */p;
|
||||
struct{ p a [257],b[9][ 5],c[5],d[5 ]; _ e,f; } i;p t[78557
|
||||
][5]; _ s,a []={ 9,9,9, 10,13,18,27 ,39,55,1,18 ,2,1,1,35,2
|
||||
,19,18,1,52 ,36,35,20,3 ,2,19,18,18 ,69,2,53,1, 52,37,3,20,
|
||||
36,19,35,18 ,52,86,2,53 ,70,1,69,38 ,4,21,3,37, 19,36,35,25
|
||||
,10,+17,15, 26,9,11,12, 23,2,06,18, 13,20,22,14 ,1,24,19,21
|
||||
|
||||
,16,5,7,4,8 ,3},u=' ';_ W(p*a,p*b){ for(p*c=a+5 ;a<c;)*a++=
|
||||
*b++;return 0; } _ O(p* a,p b){ p*c =a+5; for(; a<c&&*a-b;)
|
||||
++a; return a<c?5-(a-c) :0; } _ R(p *a, ... ) { va_list b ;
|
||||
va_start(b, a); vprintf (a,b); exit (1); } q D( p*a){ if (s
|
||||
==sizeof t/ 5)R("limit" " %d\n",s); p*b=s[t],i, j; for(i=j=
|
||||
0; i<7&&!j; ++i)if(!(j= *a==10?i:0) )*b++=u|* a ++; j-5||++
|
||||
s; } q L(p* a,_ b){ for (_ l=0,o=0, k; l<s; o=0 ){ A(i.c[j]
|
||||
|
||||
&&k-i.c[j]) A(i.a[k])A( i.d[j]&&!O( t[l],i.d[j] ))for(_ f=i
|
||||
.e; f--&&!o ; )A(i.b[f] [j]==k)if(! o){ J(o+=t[ l][j]==a[j]
|
||||
)o=o==5?b!= 5:0; } o?W( t[l],t[--s] ) :++l; } } _ e(I){ for
|
||||
(; O<D; )L[ R++]=E[O+++ D*W];return R; } q S( _ O,_*l){ for
|
||||
(_*s=&a[a[O ]],*e=a[-~O ]+s-a[O]; s <e; ){ _ i= *s++,j=i&15
|
||||
,k=l[i>>=4] <l[+j]?k=l[ i],l[i]=l[j ],l[j]=k:i; } } _ o(I){
|
||||
_ o=0, w=0; for(; o<D&w <D; )L[R++] =E[o+D*W]>= E[w+D*O]?E[
|
||||
o+++D*W]:E[ w +++ D*O]; return e(O, w,e(W,o,R,D ,L,E),D,L,E
|
||||
|
||||
); } q l( _ n,_*f){ for (_* e=f+26, i,j; f<e&&* f>u; f+= i)
|
||||
{ for(i=1,j =*f/u; i <n &&f[i]>>5== j; )++i;for (j=0; j <i;
|
||||
++j)f[j] &= 31,f[j]|=a[ f[j]+a[~-9] ]<<5; S(i,f ); } } q v(
|
||||
_ a,_*w,_*k ){ o(2,3,o( 0,1,0,a,w,k ),a,w,k); o (0,1,0,a+a,
|
||||
k,w); l(a,k ); } p*E(p* d){ _ l,j,k =u,b[u] ,c[ k]; p*e[97]
|
||||
[5 ]={ 0} ; for(l=0; l< k; ++l)c[l] =l; for(l=0 ; l<s; ++l)
|
||||
J(c[t[l][j] -97]+=k)for (l= j=7; j< 22; j+=l)S( l,c+j-l); v
|
||||
|
||||
(l,b,c);for (l= 0; l<k; ++l)b[c[l]& ~-k]=l; for (k*=3,l= 0;
|
||||
l<s; ++l) { _ i=0; J( i +=b[t[l][j] -97])if(i<k )J(e[i][ j]
|
||||
||(e[i][j]= t[l],j=4))} for(l=0; l< k; ++l)for( p*b=0,k=j=0
|
||||
; j<5&&(b=e [l][j]); k= !++j){ p c[ 32]={0} ; d =*d-120?d:b
|
||||
; if(s>2)J( k+=++c[b[j] -97]>1); if (!k) return b; } return
|
||||
d; } p*r(p* b){ _ k, l; for(_ j=k=l =0; j<5; ++ j) { signed
|
||||
char n=b[j] ,o=b[j-~5]+ ~0x66; (o?i .b[i.e]:i.c )[j]=n; !o?
|
||||
|
||||
i.a[n]=!++k :O(i.d,n)|| (*(o<0?&i.a [n]:&i.d[i. f++])=l=n);
|
||||
} i.e += !! l; L(b, k); return E("" "xyzzy"); } _ main(_ n,
|
||||
char**v){ p *j=i .c-97, *l=j-11; n- 2?R("usage" ": %s dict"
|
||||
"\n",* v):5 ; ; FILE*k= fopen(*++v, "r"); if(!k ) return //
|
||||
perror(*v), 1; ; while( fgets(j, u^ n,k)) D(j); fclose (k);
|
||||
; ; setbuf( stdout, 0); while(~-s){ printf("? " ); while(~(
|
||||
n=getchar() )&&n-10&&l< j)*l++=n; l <j&&R("inp" "ut: guess"
|
||||
" color\n") ;printf("%" ".5s\n",r(l =j+~10)); } return 0; }
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
# include <stdio.h>
|
||||
#define J(J) for (\
|
||||
_ j=0; j<5; ++j) J;
|
||||
#define/*IOCCC*/I \
|
||||
\
|
||||
\
|
||||
\
|
||||
_ W,_ O,_ R,_ D,_*L,_*E
|
||||
|
||||
|
||||
#define A(A )for(_ j=0; !o&&j<5&&(k = t[l][j]); ++j)A&&++o;
|
||||
typedef/* ; ; */void q; typedef int _ ; typedef char/* */p;
|
||||
struct{ p a [257],b[9][ 5],c[5],d[5 ]; _ e,f; } i;p t[78557
|
||||
][5]; _ s,a []={ 9,9,9, 10,13,18,27 ,39,55,1,18 ,2,1,1,35,2
|
||||
,19,18,1,52 ,36,35,20,3 ,2,19,18,18 ,69,2,53,1, 52,37,3,20,
|
||||
36,19,35,18 ,52,86,2,53 ,70,1,69,38 ,4,21,3,37, 19,36,35,25
|
||||
,10,+17,15, 26,9,11,12, 23,2,06,18, 13,20,22,14 ,1,24,19,21
|
||||
|
||||
,16,5,7,4,8 ,3},u=' ';_ W(p*a,p*b){ for(p*c=a+5 ;a<c;)*a++=
|
||||
*b++;return 0; } _ O(p* a,p b){ p*c =a+5; for(; a<c&&*a-b;)
|
||||
++a; return a<c?5-(a-c) :0; } _ R(p *a, ... ) { va_list b ;
|
||||
va_start(b, a); vprintf (a,b); exit (1); } q D( p*a){ if (s
|
||||
==sizeof t/ 5)R("limit" " %d\n",s); p*b=s[t],i, j; for(i=j=
|
||||
0; i<7&&!j; ++i)if(!(j= *a==10?i:0) )*b++=u|* a ++; j-5||++
|
||||
s; } q L(p* a,_ b){ for (_ l=0,o=0, k; l<s; o=0 ){ A(i.c[j]
|
||||
|
||||
&&k-i.c[j]) A(i.a[k])A( i.d[j]&&!O( t[l],i.d[j] ))for(_ f=i
|
||||
.e; f--&&!o ; )A(i.b[f] [j]==k)if(! o){ J(o+=t[ l][j]==a[j]
|
||||
)o=o==5?b!= 5:0; } o?W( t[l],t[--s] ) :++l; } } _ e(I){ for
|
||||
(; O<D; )L[ R++]=E[O+++ D*W];return R; } q S( _ O,_*l){ for
|
||||
(_*s=&a[a[O ]],*e=a[-~O ]+s-a[O]; s <e; ){ _ i= *s++,j=i&15
|
||||
,k=l[i>>=4] <l[+j]?k=l[ i],l[i]=l[j ],l[j]=k:i; } } _ o(I){
|
||||
_ o=0, w=0; for(; o<D&w <D; )L[R++] =E[o+D*W]>= E[w+D*O]?E[
|
||||
o+++D*W]:E[ w +++ D*O]; return e(O, w,e(W,o,R,D ,L,E),D,L,E
|
||||
|
||||
); } q l( _ n,_*f){ for (_* e=f+26, i,j; f<e&&* f>u; f+= i)
|
||||
{ for(i=1,j =*f/u; i <n &&f[i]>>5== j; )++i;for (j=0; j <i;
|
||||
++j)f[j] &= 31,f[j]|=a[ f[j]+a[~-9] ]<<5; S(i,f ); } } q v(
|
||||
_ a,_*w,_*k ){ o(2,3,o( 0,1,0,a,w,k ),a,w,k); o (0,1,0,a+a,
|
||||
k,w); l(a,k ); } p*E(p* d){ _ l,j,k =u,b[u] ,c[ k]; p*e[97]
|
||||
[5 ]={ 0} ; for(l=0; l< k; ++l)c[l] =l; for(l=0 ; l<s; ++l)
|
||||
J(c[t[l][j] -97]+=k)for (l= j=7; j< 22; j+=l)S( l,c+j-l); v
|
||||
|
||||
(l,b,c);for (l= 0; l<k; ++l)b[c[l]& ~-k]=l; for (k*=3,l= 0;
|
||||
l<s; ++l) { _ i=0; J( i +=b[t[l][j] -97])if(i<k )J(e[i][ j]
|
||||
||(e[i][j]= t[l],j=4))} for(l=0; l< k; ++l)for( p*b=0,k=j=0
|
||||
; j<5&&(b=e [l][j]); k= !++j){ p c[ 32]={0} ; d =*d-120?d:b
|
||||
; if(s>2)J( k+=++c[b[j] -97]>1); if (!k) return b; } return
|
||||
d; } p*r(p* b){ _ k, l; for(_ j=k=l =0; j<5; ++ j) { signed
|
||||
char n=b[j] ,o=b[j-~5]+ ~0x66; (o?i .b[i.e]:i.c )[j]=n; !o?
|
||||
|
||||
i.a[n]=!++k :O(i.d,n)|| (*(o<0?&i.a [n]:&i.d[i. f++])=l=n);
|
||||
} i.e += !! l; L(b, k); return E("" "xyzzy"); } _ main(_ n,
|
||||
char**v){ p *j=i .c-97, *l=j-11; n- 2?R("usage" ": %s dict"
|
||||
"\n",* v):5 ; ; FILE*k= fopen(*++v, "r"); if(!k ) return //
|
||||
perror(*v), 1; ; while( fgets(j, u^ n,k)) D(j); fclose (k);
|
||||
; ; setbuf( stdout, 0); while(~-s){ printf("? " ); while(~(
|
||||
n=getchar() )&&n-10&&l< j)*l++=n; l <j&&R("inp" "ut: guess"
|
||||
" color\n") ;printf("%" ".5s\n",r(l =j+~10)); } return 0; }
|
||||
|
||||
|
||||
|
||||
+23999
File diff suppressed because it is too large.
Load diff
+23783
File diff suppressed because it is too large.
Load diff
+25469
File diff suppressed because it is too large.
Load diff
+24014
File diff suppressed because it is too large.
Load diff
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
: ${dir:=results}
|
||||
word=$1
|
||||
echo $word | grep -q /
|
||||
if test $? -eq 1; then word=$dir/$word; fi
|
||||
|
||||
grep good: $word | awk '{n[$5]++}END{for(i=1; i<7; ++i)print i,n[i]}' | sort -k1n
|
||||
grep lose: $word | awk '{print $NF}' | pr -t -a -5
|
||||
@@ -0,0 +1,26 @@
|
||||
/* clang-format --style=LLVM < prog.c | reveal */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int start = 0, len;
|
||||
char buf[128];
|
||||
|
||||
while (fgets(buf, sizeof buf, stdin)) {
|
||||
len = strlen(buf);
|
||||
if (len > 3 && !isspace(buf[0])) {
|
||||
char c = buf[2];
|
||||
if (c == 'i') start = 1;
|
||||
if (c == 'm') start = 0;
|
||||
if (!start) continue;
|
||||
if (buf[2] == '*') c = buf[3];
|
||||
if (isalpha(c)) putchar(c);
|
||||
} else if (start && len > 6 && buf[6] == ']')
|
||||
putchar(buf[4]);
|
||||
}
|
||||
putchar('\n');
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* run - connect gen and prog via pipes
|
||||
*
|
||||
* one-shot: run dict
|
||||
* specific: run dict $start $target
|
||||
* automate: cat dict | while read a; do run dict $start $a; done
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
static int DB;
|
||||
|
||||
static int
|
||||
error(char *who)
|
||||
{
|
||||
perror(who);
|
||||
kill(0, SIGKILL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
xread(int fd, char *buf, int len)
|
||||
{
|
||||
int remain = len;
|
||||
int t = 0;
|
||||
|
||||
if (DB) { printf("xread %d:", len); fflush(stdout); }
|
||||
|
||||
do {
|
||||
int n = read(fd, buf + t, remain);
|
||||
if (n < 0) error("read");
|
||||
if (n == 0) return t;
|
||||
if (n <= remain) {
|
||||
remain -= n;
|
||||
t += n;
|
||||
}
|
||||
} while (t != len);
|
||||
buf[t] = 0;
|
||||
|
||||
if (DB) {
|
||||
printf(" %s", buf);
|
||||
if (buf[t-1] != '\n') printf("\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void
|
||||
spawn(int r[2], int w[2], char **ev, char *path, char *name, char *dict, char *opt)
|
||||
{
|
||||
pipe(r) && error("pipe r");
|
||||
pipe(w) && error("pipe w");
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1)
|
||||
error("fork");
|
||||
|
||||
if (pid == 0) {
|
||||
char *v[4] = { name, dict, opt, 0 };
|
||||
|
||||
dup2(r[0], 0) < 0 && error("dup2 r[0]");
|
||||
dup2(w[1], 1) < 0 && error("dup2 w[1]");
|
||||
|
||||
close(r[1]);
|
||||
close(w[0]);
|
||||
|
||||
execve(path, v, ev);
|
||||
error(path);
|
||||
}
|
||||
|
||||
close(r[0]);
|
||||
close(w[1]);
|
||||
}
|
||||
|
||||
int
|
||||
main(int ac, char **av, char **ev)
|
||||
{
|
||||
int pgr[2], pgw[2]; // gen r/w
|
||||
int ppr[2], ppw[2]; // prog r/w
|
||||
|
||||
static char guess[20], report[20];
|
||||
static char xyzzy[] = "xyzzy";
|
||||
char *cp, *prog = "./prog";
|
||||
int n, end = 0;
|
||||
int nguesses = 6;
|
||||
|
||||
if (ac < 2) {
|
||||
fprintf(stderr, "usage: %s dict [start [target]]\n", av[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DB = !!getenv("DEBUG");
|
||||
|
||||
if ((cp = getenv("GUESSES")))
|
||||
nguesses = atoi(cp);
|
||||
if ((cp = getenv("PROG")))
|
||||
prog = cp;
|
||||
|
||||
spawn(pgr, pgw, ev, "./gen", "gen", av[1], ac==4 ? av[3] : 0);
|
||||
spawn(ppr, ppw, ev, prog, "prog", av[1], 0);
|
||||
|
||||
// wait for gen to be ready; read the prompt
|
||||
char buf[20];
|
||||
xread(pgw[0], buf, 2);
|
||||
if (buf[0] != '?') {
|
||||
xread(pgw[0], buf+2, 12); // target: guess
|
||||
printf("gen: %s", buf);
|
||||
xread(pgw[0], buf, 2);
|
||||
}
|
||||
|
||||
// wait for prog prompt
|
||||
xread(ppw[0], buf, 2);
|
||||
|
||||
memcpy(guess, ac>2 ? av[2] : "blast", 5);
|
||||
guess[5] = '\n'; // mimic reading from stdin
|
||||
|
||||
for (int i = 0; i < nguesses && end != 1; ++i) {
|
||||
printf("guess%d: %s", i+1, guess);
|
||||
|
||||
n = write(pgr[1], guess, 6);
|
||||
if (n < 0) error("write pgw[1]");
|
||||
n = xread(pgw[0], report, 12);
|
||||
if (n != 12) {
|
||||
if (n < 0) error("read pgr[0]");
|
||||
printf("bad read from pgr[0]: %d\n", n);
|
||||
return 2;
|
||||
}
|
||||
|
||||
printf("gen: %s", report);
|
||||
|
||||
xread(pgw[0], guess, 2); // read the prompt
|
||||
if (guess[0] != '?') {
|
||||
xread(pgw[0], guess+2, 14);
|
||||
printf("gen result end=%d: %s", end, guess);
|
||||
if (end == 2) {
|
||||
close(pgr[1]);
|
||||
close(ppr[1]);
|
||||
return 0;
|
||||
}
|
||||
close(pgw[0]);
|
||||
close(pgr[1]);
|
||||
end = 1;
|
||||
}
|
||||
|
||||
if (end == 2) {
|
||||
// cannot write to prog, it exited on winner, but gen disagrees
|
||||
printf("ERROR: prog reported win, gen does not\n");
|
||||
close(pgr[1]);
|
||||
close(ppr[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
n = write(ppr[1], report, n);
|
||||
if (n < 0) error("ppw[1]");
|
||||
n = xread(ppw[0], guess, 6);
|
||||
if (n != 6) {
|
||||
if (n < 0) error("read ppr");
|
||||
printf("bad read from pipe_prog: %d {%.*s}\n", n, n, guess);
|
||||
return 3;
|
||||
}
|
||||
if (strncmp(guess, xyzzy, sizeof(xyzzy)-1) == 0) {
|
||||
printf("ERROR: desired word not in this dictionary\n");
|
||||
close(pgr[1]);
|
||||
close(ppr[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// might not prompt, just exit
|
||||
if (xread(ppw[0], report, 2) == 0) { // read the prompt
|
||||
printf("prog: EOF\n");
|
||||
if (end == 1) {
|
||||
close(pgr[1]);
|
||||
close(ppr[1]);
|
||||
return 0;
|
||||
}
|
||||
end = 2;
|
||||
close(ppr[1]);
|
||||
close(ppw[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
blast ybybb
|
||||
amber gbyyb
|
||||
adobe gygyg
|
||||
abode ggggg
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
blast bbybg
|
||||
daunt bgggg
|
||||
haunt bgggg
|
||||
gaunt bgggg
|
||||
vaunt bgggg
|
||||
taunt ygggg
|
||||
@@ -0,0 +1,3 @@
|
||||
terns ybbbb
|
||||
loath gbbyy
|
||||
light ggggg
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# try.sh - demonstrate 2024/burton
|
||||
|
||||
# make sure CC is set so that when we do make CC="$CC" it isn't empty. Doing it
|
||||
# this way allows us to have the user specify a different compiler in an easy way.
|
||||
#
|
||||
[[ -z "$CC" ]] && CC="cc"
|
||||
|
||||
# make sure the program has been built
|
||||
#
|
||||
make CC="$CC" all >/dev/null || exit 1
|
||||
|
||||
# clear the screen
|
||||
#
|
||||
clear
|
||||
|
||||
# try the program
|
||||
#
|
||||
read -r -n 1 -p "Press any key to run: ./prog: "
|
||||
echo 1>&2
|
||||
echo 1>&2
|
||||
cat test.light 1>&2
|
||||
echo 1>&2
|
||||
echo '=-=-=' 1>&2
|
||||
echo 1>&2
|
||||
./prog dict < test.light
|
||||
echo 1>&2
|
||||
@@ -0,0 +1,165 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef void q;
|
||||
typedef int _;
|
||||
typedef char p;
|
||||
|
||||
struct {
|
||||
p a[256], b[9][5], c[5], d[5];
|
||||
_ e, f;
|
||||
} i;
|
||||
p t[78557][5];
|
||||
_ s, a[] = {
|
||||
9, 9, 9, 10, 13, 18, 27, 39, 55, 1, 18, 2, 1, 1,
|
||||
35, 2, 19, 18, 1, 52, 36, 35, 20, 3, 2, 19, 18, 18,
|
||||
69, 2, 53, 1, 52, 37, 3, 20, 36, 19, 35, 18, 52, 86,
|
||||
2, 53, 70, 1, 69, 38, /* 36 */ 4, 21, 3, 37, 19,
|
||||
36, 35, 25, 10, 17, 15, 26, 9, 11, 12, 23, 2, 6, 18,
|
||||
13, 20, 22, 14, 1, 24, 19, 21, 16, 5, 7, 4, 8, 3
|
||||
} , u = ' ';
|
||||
|
||||
_ W(p *a, p *b) {
|
||||
for (p *c = a + 5; a < c; )
|
||||
*a++ = *b++;
|
||||
return 0;
|
||||
}
|
||||
_ O(p *a, p b) {
|
||||
p *c = a + 5;
|
||||
for (; a < c && *a - b; )
|
||||
++a;
|
||||
return a < c ? 5 - (a - c) : 0;
|
||||
}
|
||||
_ R(p *a, ...) {
|
||||
va_list b;
|
||||
va_start(b, a);
|
||||
vprintf(a, b);
|
||||
exit(1);
|
||||
}
|
||||
q D(p *a) {
|
||||
if (s == sizeof t / 5)
|
||||
R("limit %d\n", s);
|
||||
p *b = s[t], i, j;
|
||||
for (i = j = 0; i < 7 && !j; ++i)
|
||||
if (!(j = *a == 10 ? i : 0))
|
||||
*b++ = u | *a++;
|
||||
j - 5 || ++s;
|
||||
}
|
||||
q L(p *a, _ b) {
|
||||
for (_ l = 0, o = 0, k; l < s; o = 0) {
|
||||
for (_ j = 0; !o && j < 5 && (k = t[l][j]); ++j)
|
||||
i.c[j] && k - i.c[j] && ++o;
|
||||
for (_ j = 0; !o && j < 5 && (k = t[l][j]); ++j)
|
||||
i.a[k] && ++o;
|
||||
for (_ j = 0; !o && j < 5 && (k = t[l][j]); ++j)
|
||||
i.d[j] && !O(t[l], i.d[j]) && ++o;
|
||||
for (_ f = i.e; f-- && !o; )
|
||||
for (_ j = 0; !o && j < 5 && (k = t[l][j]); ++j)
|
||||
i.b[f][j] == k && ++o;
|
||||
if (!o) {
|
||||
for (_ j = 0; j < 5; ++j)
|
||||
o += t[l][j] == a[j];
|
||||
o = o == 5 ? b != 5 : 0;
|
||||
}
|
||||
o ? W(t[l], t[--s]) : ++l;
|
||||
}
|
||||
}
|
||||
_ e(_ W, _ O, _ R, _ D, _ *L, _ *E) {
|
||||
for (; O < D; )
|
||||
L[R++] = E[O++ + D * W];
|
||||
return R;
|
||||
}
|
||||
q S(_ O, _ *l) {
|
||||
for (_ *s = &a[a[O]], *e = a[-~O] + s - a[O]; s < e; ) {
|
||||
_ i = *s++,
|
||||
j = i & 15,
|
||||
k = l[i >>= 4] < l[j] ? k = l[i], l[i] = l[j], l[j] = k : i;
|
||||
}
|
||||
}
|
||||
_ o(_ W, _ O, _ R, _ D, _ *L, _ *E) {
|
||||
_ o = 0, w = 0;
|
||||
for (; o < D & w < D; )
|
||||
L[R++] = E[o + D * W] >= E[w + D * O]
|
||||
? E[o++ + D * W]
|
||||
: E[w++ + D * O];
|
||||
return e(O, w, e(W, o, R, D, L, E), D, L, E);
|
||||
}
|
||||
q l(_ n, _ *f) {
|
||||
for (_ *e = f + 26, i, j; f < e && *f > u; f += i) {
|
||||
for (i = 1, j = *f / u; i < n && f[i] >> 5 == j; )
|
||||
++i;
|
||||
for (j = 0; j < i; ++j)
|
||||
f[j] &= 31,
|
||||
f[j] |= a[f[j] + a[~-9]] << 5;
|
||||
S(i, f);
|
||||
}
|
||||
}
|
||||
q v(_ a, _ *w, _ *k) {
|
||||
o(2, 3, o(0, 1, 0, a, w, k), a, w, k);
|
||||
o(0, 1, 0, a + a, k, w);
|
||||
l(a, k);
|
||||
}
|
||||
p *E(p *d) {
|
||||
_ l, j, k = u, b[u], c[k];
|
||||
p *e[97][5] = { 0 } ;
|
||||
for (l = 0; l < k; ++l)
|
||||
c[l] = l;
|
||||
for (l = 0; l < s; ++l)
|
||||
for (_ j = 0; j < 5; ++j)
|
||||
c[t[l][j] - 97] += k;
|
||||
for (l = j = 7; j < 22; j += l)
|
||||
S(l, c + j - l);
|
||||
v(l, b, c);
|
||||
for (l = 0; l < k; ++l)
|
||||
b[c[l] & ~-k] = l;
|
||||
for (k *= 3, l = 0; l < s; ++l) {
|
||||
_ i = 0;
|
||||
for (_ j = 0; j < 5; ++j)
|
||||
i += b[t[l][j] - 97];
|
||||
if (i < k)
|
||||
for (_ j = 0; j < 5; ++j)
|
||||
e[i][j] || (e[i][j] = t[l], j = 4);
|
||||
}
|
||||
for (l = 0; l < k; ++l)
|
||||
for (p *b = 0, k = j = 0; j < 5 && (b = e[l][j]); k = !++j) {
|
||||
p c[32] = { 0 } ;
|
||||
d = *d - 120 ? d : b;
|
||||
if (s > 2)
|
||||
for (_ j = 0; j < 5; ++j)
|
||||
k += ++c[b[j] - 97] > 1;
|
||||
if (!k)
|
||||
return b;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
p *r(p *b) {
|
||||
_ k, l;
|
||||
for (_ j = k = l = 0; j < 5; ++j) {
|
||||
signed char n = b[j], o = b[j - ~5] + ~0x66;
|
||||
(o ? i.b[i.e] : i.c)[j] = n;
|
||||
!o ? i.a[n] = !++k : O(i.d, n) || (*(o < 0 ? &i.a[n] : &i.d[i.f++]) = l = n);
|
||||
}
|
||||
i.e += !!l;
|
||||
L(b, k);
|
||||
return E("xyzzy");
|
||||
}
|
||||
_ main(_ n, char **v) {
|
||||
p *j = i.c - 97, *l = j - 11;
|
||||
n - 2 ? R("usage: %s dict\n", *v) : 5;
|
||||
FILE *k = fopen(*++v, "r");
|
||||
if (!k)
|
||||
return perror(*v), 1;
|
||||
while (fgets(j, u ^ n, k))
|
||||
D(j);
|
||||
fclose(k);
|
||||
setbuf(stdout, 0);
|
||||
while (~-s) {
|
||||
printf("? ");
|
||||
while (~(n = getchar()) && n - 10 && l < j)
|
||||
*l++ = n;
|
||||
l < j && R("input: guess color\n");
|
||||
printf("%.5s\n", r(l = j + ~10));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
test $# -eq 6 || { echo must have all six scores; exit 1; }
|
||||
echo "($1*1 + $2*2 + $3*3 + $4*4 + $5*5 + $6*6) / ($1+$2+$3+$4+$5+$6)" | bc -l | awk '{printf "%.04f\n", $1 }'
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
# usage: echo terns | word.result
|
||||
# usage: cat dict | word.result
|
||||
|
||||
dir=$1
|
||||
: ${dir:=results}
|
||||
|
||||
d=$2
|
||||
: ${d:=dict}
|
||||
|
||||
# for each word as a starting guess
|
||||
# run the sim for each target word
|
||||
|
||||
while read w; do
|
||||
echo $w;
|
||||
cat dict | while read t; do
|
||||
./run $d $w $t
|
||||
done > $dir/$w
|
||||
done
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
dir=$1
|
||||
: ${dir:=results}
|
||||
lines=$2
|
||||
: ${lines:=10}
|
||||
|
||||
for name in $dir/*; do
|
||||
grep lose: $name | awk '{print $NF}';
|
||||
done |
|
||||
awk '{a[$1]++}END{for(i in a)print i,a[i]}' |
|
||||
sort -k2nr |
|
||||
pr -t -a -5 |
|
||||
sed ${lines}q
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
a=$(./rank results/$1 | awk 'NR<7 {print $2}')
|
||||
/bin/echo -n "$1 "
|
||||
./wavg $a
|
||||
Reference in new issue
Block a user