Shell输出成语列表用于填字游戏生成坐标
之前说到根据成语列表生成填字游戏的坐标,这次来说下成语列表需要如何得到,代码:
#!/bin/bash
# idiom_solitaire_sampler.sh by jtwo
idiom_number=${1:-5} #默认成语个数
topic_number=${2:-10} #默认出题次数
SQL='mysql -uroot -h127.0.0.1 -D crossword -Ne'; export MYSQL_PWD=123456 #隐藏警告
LSR() { s="${1}"; echo "$[$s-1],${s},$[$s+1]"; } #左右索引 Left Self Right
RC() { echo "${1}" |tr ',' '\n' |shuf |head -1; } #随机选择逗号分隔列表 Random Choice
POP_RC() { echo "${1}" |tr ',' '\n' |grep -Ev $(echo "${2}" |tr ',' '|') |shuf |head -1; } #先POP再RC
RELK() { k="${1}"; v="${2}"; for x in {1..4}; do [[ "$x" -eq "$k" ]] && echo -n "$v" || echo -n "_"; done; } #匹配模式 Regex Like
PI() { $SQL "SELECT idiom FROM paraphrase WHERE idiom LIKE '${1}' AND idiom NOT IN ('${idiom_list}') ORDER BY RAND() LIMIT 1"; } #Pick Idiom
topic_finish=0 #记录成功出题数量(初值)
while [[ $topic_finish -lt $topic_number ]]; do
debug_line="========================================"
idiom_1st=$(PI "____") #首先随机生成一个成语
idiom_list=${idiom_1st} #存放所有抽中成语的列表
pos_1st_tail=$(RC 1,2,3,4) #随机选出成语中某字的索引
pos_2nd_head=$(RC 1,2,3,4) #第二个成语的交叉点的位置
case $pos_1st_tail in #获取第二个成语的尾部坐标
1|4 ) pos_2nd_tail=$(POP_RC "1,2,3,4" "$pos_2nd_head") ;;
2|3 ) pos_2nd_tail=$(POP_RC "1,2,3,4" $(LSR "$pos_2nd_head")) ;;
esac
pos_2nd_diff=$[$pos_2nd_tail-$pos_2nd_head] #是否紧邻(绝对值)、是否回拐(负数)
if [[ ${pos_2nd_diff#-} -eq 1 ]]; then #头尾紧邻,前后成语头尾相接
pos_3rd_head=$(echo 1,4 |tr ',' '\n' |grep -Ev "2|3|$pos_1st_tail")
else
pos_3rd_head=$(RC 1,2,3,4) #如果头尾不是紧邻,那就可以任选位置
fi
IFTF=1 #是否首次生成本题 Is First Time Flag
idiom_finish=0 #记录成语个数(算法至少四个成语)
while [[ $idiom_finish -le $idiom_number ]]; do
case $pos_2nd_tail in #获取第三个成语的尾部坐标
1|4 ) pos_3rd_tail=$(POP_RC "1,2,3,4" "$pos_3rd_head") ;;
2|3 ) pos_3rd_tail=$(POP_RC "1,2,3,4" $(LSR "$pos_3rd_head")) ;;
esac
pos_3rd_diff=$[$pos_3rd_tail-$pos_3rd_head] #是否紧邻(绝对值)、是否回拐(负数)
#【开始,特殊逻辑】第四个成语,根据回拐情况限制索引
if [[ ${pos_3rd_diff} -lt 0 ]]; then #成语方向左拐(默认方向:右)
pos_1st_diff=$[$pos_1st_tail-1]
else
pos_1st_diff=$[4-$pos_1st_tail]
fi
if [[ ${pos_1st_diff} -gt ${pos_3rd_diff#-} ]]; then
pos_1st_diff=${pos_3rd_diff#-} #以最小间隔为边长
fi
if [[ ${pos_2nd_diff} -lt 0 ]]; then #成语方向上拐(默认方向:下)
pos_4th_head_lv=$[ (${pos_3rd_diff#-}+${pos_2nd_diff#-}-${pos_1st_diff}-6) / (-1) ]
[[ $pos_4th_head_lv -lt 1 ]] && pos_4th_head_lv=1 #限制坐标,可能为0(Limit Value)
pos_4th_head_lv=$(printf "%s," $(seq $[$pos_4th_head_lv] 4) |sed 's/,\+$//g')
else
pos_4th_head_lv=$[ ${pos_3rd_diff#-}+${pos_2nd_diff#-}-${pos_1st_diff}-1 ]
[[ $pos_4th_head_lv -gt 4 ]] && pos_4th_head_lv=4 #限制坐标,可能为5(Limit Value)
pos_4th_head_lv=$(printf "%s," $(seq 1 $[$pos_4th_head_lv]) |sed 's/,\+$//g')
fi
#【结束,特殊逻辑】第四个成语,根据回拐情况限制索引
if [[ ${pos_3rd_diff#-} -eq 1 ]]; then #头尾紧邻,前后成语头尾相接(1或4)
pos_4th_head=$(echo $pos_4th_head_lv |tr ',' '\n' |grep -Ev "2|3|$pos_2nd_tail")
else
pos_4th_head=$(RC $pos_4th_head_lv) #如果头尾不是紧邻,那就可以任选位置
fi
if [[ $IFTF -eq 1 ]]; then #首次运行才需要处理"第1、2、3个"成语,并记录到输出列表
word_1st_tail=${idiom_1st:$[${pos_1st_tail}-1]:1} #取出第一个成语的交接点的字,从零索引减一
idiom_2nd=$(PI $(RELK $pos_2nd_head $word_1st_tail))
[[ -z "$idiom_2nd" ]] && break || idiom_list="${idiom_list}','${idiom_2nd}"
[[ $idiom_number -eq 2 ]] && echo $idiom_list |tr -d "'" && ((topic_finish++)) && break
word_2nd_tail=${idiom_2nd:$[${pos_2nd_tail}-1]:1}
idiom_3rd=$(PI $(RELK $pos_3rd_head $word_2nd_tail))
[[ -z "$idiom_3rd" ]] && break || idiom_list="${idiom_list}','${idiom_3rd}"
[[ $idiom_number -eq 3 ]] && echo $idiom_list |tr -d "'" && ((topic_finish++)) && break
word_3rd_tail=${idiom_3rd:$[${pos_3rd_tail}-1]:1}
fi
idiom_4th=$(PI $(RELK $pos_4th_head $word_3rd_tail)); [[ -z "$idiom_4th" ]] && break
idiom_list="${idiom_list}','${idiom_4th}"
idiom_finish=$[$(echo $idiom_list |grep -Eo , |wc -l) + 1] #接龙的长度
[[ $idiom_finish -eq $idiom_number ]] && echo $idiom_list |tr -d "'" && ((topic_finish++)) && break
if [[ $idiom_number -ge 5 ]]; then
idiom_1st=$idiom_2nd #更新冗余数据
idiom_2nd=$idiom_3rd
idiom_3rd=$idiom_4th
pos_1st_tail=$pos_2nd_tail #交替坐标
pos_2nd_head=$pos_3rd_head
pos_2nd_tail=$pos_3rd_tail
pos_3rd_head=$pos_4th_head
pos_2nd_diff=$[$pos_2nd_tail-$pos_2nd_head] #因为在循环之外所以需要更新
word_3rd_tail=${idiom_3rd:$[${pos_3rd_tail}-1]:1} #需要接上"第四个"成语
IFTF=0 #不是首次运行,后续只需处理"第四个"成语(任意非1值均可,仅作判断)
fi
done
done
执行命令:
echo -e '3 3\n4 4\n5 5' |while read x y; do bash idiom_solitaire_sampler.sh $x $y; done
输出效果:
杀身报国,感恩图报,感旧之哀
议论风生,风栉雨沐,础润而雨
桀犬吠尧,蜀犬吠日,日月经天
负薪之忧,牛之一毛,颠毛种种,谬种流传
冰炭不投,投鞭断流,流金铄石,金貂换酒
子为父隐,言为心声,声动梁尘,动辄得咎
黑白混淆,论黄数黑,笃而论之,笃近举远
窥间伺隙,管窥蠡测,持蠡测海,扶颠持危,两瞽相扶
贪夫徇财,夫子自道,天道好还,天命有归,海阔天高
髀肉复生,慢易生忧,忧心如焚,如日方中,外方内圆
贵古贱今,年逾古稀,年高德劭,爱人以德,以古为镜
怨天怨地,以怨报德,酒后无德,后生小子,福过灾生