![AWK 帮助解决 Wordle 谜题](https://linux22.com/image/202939/AWK%20%E5%B8%AE%E5%8A%A9%E8%A7%A3%E5%86%B3%20Wordle%20%E8%B0%9C%E9%A2%98.png)
我见过好几个文章描述基本的 Linux 命令(grep 和 regex)如何解决 Wordle 谜题。如果您不熟悉游戏,请查看本问题末尾的注释。我决定使用 awk 来达到这个目的。这是我的第一次尝试:
awk 'length($0)==5' /usr/share/dict/words | awk '!/\-|\x27|[A-Z]/' | awk '!/[isbou]/' | awk '/a/&&/l/&&/e/&&/e/&&/t/' | awk '/...a/' | cat -n
前两个 awk 从标准 Ubuntu 安装中的字典中生成 5 个字母的单词列表(不包括大写单词、连字符、撇号)。
awk '/a/&&/l/&&/e/&&/t/'
(对于黄色字母)过滤那些有字母的单词——在单词中但不在书写位置。在此示例中,字母 a、l、e、t
awk '!/[isbou]/'
(对于灰色字母)过滤掉包含任何这些字母的单词 - 在此示例中为 i、s、b、o、u
awk '/...a/'
(对于绿色字母)表示存在且位于正确位置的字母。
我决定使用 5 个字母的单词列表作为我的字典,以保持代码简单。您可以通过多种方式准备这样的列表。例如:
awk 'length($0)==5' /usr/share/dict/words | awk '!/\-|\x27|[A-Z]/' > fives.txt
或者
curl -s 'https://random-word-api.herokuapp.com/word?number=100000' | awk -v RS=',' '{gsub(/[]["]+/,"")}1' | awk 'length($i)==5' > fives.txt
将此文件( Fives.txt )放在主目录中,命令变得更短:
cat ~/fives.txt | awk '!/[isbou]/' | awk '/a/&&/l/&&/e/&&/e/&&/t/' | awk '/...a/' | cat -n
这效果很好。但这可以缩短吗?如何使黄色字母的检查更加优雅,并且在编辑下一次猜测的命令时不易出现键入错误?
注意:规则非常简单:您需要在 6 次尝试中猜出隐藏的单词(通常是 5 个)。首先,只需在第一行输入任何单词即可。如果该字母被正确猜测并且位于正确的位置,它将以绿色突出显示,如果该字母在单词中,但在错误的位置 - 以黄色突出显示,如果该字母不在单词中,它将保留灰色的。你能在 6 次尝试中猜出隐藏的单词吗?
答案1
您可以通过组合所有 AWK 过滤器并合并cat
功能来缩短此时间:
awk '!/[isbou]/ && /a/&&/l/&&/e/&&/t/ && /^...a.$/ { printf("%6d %s\n", ++i, $0) }' ~/fives.txt
我固定了绿色过滤器来专门检查五个位置。
我不会尝试进一步简化 AWK 部分,而是使用带有三个参数的 shell 脚本:灰色字母列表、黄色字母列表以及正确位置的绿色字母。
#!/bin/bash
awkcmd=""
if [[ ${#1} -gt 0 ]]; then
awkcmd="${awkcmd}!/[$1]/ &&"
fi
if [[ ${#2} -gt 0 ]]; then
for (( i=0; i < ${#2}; i++ )); do
awkcmd="${awkcmd} /${2:$i:1}/ &&"
done
fi
if [[ ${#3} -gt 0 ]]; then
awkcmd="${awkcmd} /$3/"
else
awkcmd="${awkcmd} 1"
fi
awkcmd="${awkcmd} { printf(\"%6d %s\n\", ++i, \$0) }"
awk "$awkcmd" ~/fives.txt
对于更通用的单词列表,请awkcmd="/^[a-z]{5}$/ &&"
在第 3 行中使用。
答案2
一个非常粗糙的脚本(更像是一个存根)可以这样开始:
#!/bin/bash
conditions='/^[a-z]{5}$/'
while getopts "c:i:e:" opt; do case "${opt}" in
c) conditions+=" && /^${OPTARG,,}/" ;;
i) conditions+=$(sed 's|.| \&\& /&/|g' <<<"${OPTARG,,}") ;;
e) conditions+=" && !/[${OPTARG,,}]/" ;;
esac; done
shift $((OPTIND-1))
awk "${conditions} { print toupper(\$0) }" /usr/share/dict/words
调用./wordle.sh -c "...a" -i "alet" -e "isbou"
会产生:
CLEAT
FETAL
METAL
PETAL
PLEAT
本例中生成的 awk 为:
awk '/^[a-z]{5}$/ && /^...a/ && /a/ && /l/ && /e/ && /t/ && !/[isbou]/ { print toupper($0) }' /usr/share/dict/words
...对脚本的草率调用将是./wordle.sh -c...a -ialet -eisbou
当然,{ print toupper(\$0) }
脚本部分完全是装饰性的,可以删除。它是由于 Wordle 选择在 gui 中仅使用大写字母而添加的。 :)
答案3
排除任何内容是没有意义的,因为您已经只有 5 个字母的单词,因此!/[isbou]/
如果您随后检查/a/&&/l/&&/e/&&/e/&&/t/
.接下来,重复/e/ && /e/
是没有意义的,它不会检查两个e
,它只检查一个,但会检查同一件事两次。如果您想查找包含两个e
的案例,您需要/e.*e/
.因此,将所有这些放在一起,这可能就是您想要的:
awk '/...a./ && /l/ &&/e.*e/ && /t/{print ++k,$0}' ~/fives.txt
答案4
如果你只想写这个:
awk 'length($0)==5' /usr/share/dict/words | awk '!/[A-Z]/' | awk '/[:alpha:]/' | awk '!/[isbou]/' | awk '/a/&&/l/&&/e/&&/e/&&/t/' | awk '/...a/' | cat -n
更简洁地说,它是:
awk '!/[A-Zisbou]/ && /l/&&/e/&&/t/ && /^...a.$/ {print ++n, $0}' /usr/share/dict/words
但如果你想排除包含非小写字母字符的“单词”,那就是:
awk '/^[[:lower:]]+$/ && /l/&&/e/&&/t/ && /^...a.$/ {print ++n, $0}' /usr/share/dict/words
或者如果您愿意:
awk '/l/&&/e/&&/t/ && /^[[:lower:]]{3}a[[:lower:]]$/ {print ++n, $0}' /usr/share/dict/words