パーソナルブログメモリ

a = [1, 1]
for _ in "*" * 999: a += [sum(a[-2:])]
print(a)

CodinGame Wordleでアルゴリズムを作っていく過程を小公開

2023-03-24 | 新規1000万人ぐらいにプログラムをマスターしてもらいたい

CodinGameに新しく登場したWordleマスターマインドの文字列版

固定文字数の文字列を推測とそれによって与えられるヒントで

単手数で答えたほうが勝ち

 

 

旧バージョン

現在はオプティマイズゲームになっていますが、最初は対戦型ゲーム

対戦型の時はルーチンをいくつか作り1位を保ってました

答えは事前に与えられる単語集の中にありました

 

 

新バージョン

単語集が与えられず

SUBMIT後は問題すらわからず、エラー時のログもだせません

最初これではゲームにならないかなと放置、

正直なところWordleの投稿か承認されない可能性もありそうと思っていました

 

 

機械学習

承認されたタイミングで再開発してみました

今回は少し機械学習も使ってみることにします...(CharGPTさん活用)

 

aからzまでの文字列を使われる頻度の高い順にならべかえられますか?と質問してその解答

これらの文字を含む文字列を頻度の高い順に並べると、以下のようになります。

e, a, t, o, i, n, s, h, r, d, l, u, c, m, f, w, y, g, p, b, v, k, x, j, q, z

これを単語の生成ルーチンと初期投入に導入してみました

(本来の機械学習とは意味が違うような...)

 

 

序盤解答

初期投入はこの文字列を頭から順番に答え、必要文字を絞り込みます

 

初期文字列生成

文字列の生成は再帰で作成

頻度順に文字を並べていき必要文字列が揃った段階で

過去、全解答とヒントに突き合わせ、矛盾のない最初のものを解答

 

文字生成のが多くなるパターンでタイムアウトを発生

暫定的に定時で最後に作ったものを提示

 

 

SUBMITしてバグ

全問解けていない 105人中96位

SUBMIT後が検証する方法がなく、まっくらな洞窟で手探り開始の気分

 

 

シミュレーション作成

最終的にローカルで問題生成から解答まで検証するシミュレーションを作成しました

このゲームのサーバー側の作業は少ないのでなんとかなりました

 

何度か動かして、ソースと にらめっこした結果

抜け落ちるケースが判明 もれを塞いだ所なんとなく直ります(一個余分なreturn)

再帰系のプログラムだと直接の原因はわかりにくい

バグが取れて 18位ぐらい

 

文字生成をバラつかせる

次にタイムアウトをなくしたい

文字生成で解答の頻度が少ないものを後回しにできないか、一日ぐらい頭の中で検討

 

複数回、同じ英字が発生している時、その検索段階の文字列を一時保管して

生成文字を初期段階でバラつかせる

 

他にもシミュレーションの回数を複数回して平均回数を測定

 

解答ターン数は平均9.5回から9.1回に改善しました

 

結果

初期解答のルーチンも検討してみましたが、

これだと思ったルーチンは平均11.5回で 妄想通りにはいかない

 

ということで 10位まであがる 寝て起きて 106人中11位

 

追加案検討

あるていど単語に意味があるなら、

単語集をこちらでつくって、そこから答えるという案はある

ロジックはそんなに大変ではないけど、単語集作成が大変

(追記 8文字の英単語606個 追加、これで1位いただき!

とか妄想したけど1点もあがらず、サーバー側の文字列ランダムっぽい)

 

シミュレーション版のソースを最後に残しときます

 

import sys
import math
import time
import random

def get_guess(words,states):
    global az,answer,same3

    if len(az)>=word_length:
        rw=az[:word_length]
        az=az[word_length:]
        answer+=[rw]
        return
    
    r=""
    word_check_rec("",True)
    if len(answer)>0:return
    for w in same3:
        word_check_rec(w,False)
        if len(answer)>0:return
    return

def check_same3(word):
    return len(word)-len(set(list(word)))>=1

def word_check_rec(word,same3_sw):
    global answer,same3
    if answer!=[]:return
    if same3_sw and check_same3(word):
        same3+=[word]
        #ep("same3 打ち切り",word)
        return
    if len(set(list(word)) | s_use)>word_length:
        ep("打ち切り")
        return
    l=len(word)
    if l==word_length:return
    #ep(word,hit,l,hit[l])
    if hit[l]!="*":
        if l+1==word_length:
            if check(word+hit[l]):answer+=[word+hit[l]]
        elif l+1<word_length:
            word_check_rec(word+hit[l],same3_sw)
    else:
        for a in ab:
            if a not in no_use_line[l]:
                if l+1==word_length:
                    if check(word+a):answer+=[word+a]
                elif l+1<word_length:
                    word_check_rec(word+a,same3_sw)
    return

def check(word):
    global check_count,u_count
    if len(set(list(word)) | s_use)>word_length:
        u_count+=1
        return False
    check_count+=1
    if check_count%100==0:
        ep(check_count,word,set(list(word)) | s_use)
        if time.time()-stime>0.040:
            ep("無理やり")
            return True
    for w,st in zip(words,states):
        if get_state(word,w)!=st:return False
    return True

def get_state(answer_word,word):
    r=[]
    for ca,c in zip(answer_word,word):
        if ca==c:r+=[3]
        elif answer_word.find(c)>=0:r+=[2]
        else:r+=[1]
    return r

def ep(*t):print(t,file=sys.stderr)

def ret_guess(guess):
    if guess=="":
        return " ".join(list("0"*word_length))
    else:
        return " ".join(str(i) for i in get_state(q_word,guess))

def random_word(n):
    a=list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
    r=""
    for i in range(n):
        random.shuffle(a)
        r+=a[0]
    return r

def last1(hit):
    for uc in use:
        line=[]
        for i,c in enumerate(hit):
            if c==uc:line+=[-1]
            if c!="*":continue
            if uc not in no_use_line[i]:
                line+=[i]
        if len(line)==1:
            p=line[0]
            if p!=-1:
                nhit=hit[:p]+uc+hit[p+1:]
                ep("@@@ nhit,hit,q_word @@@",nhit,hit,q_word)
                hit=nhit
    return hit

time_list=[]
for world in range(100):
    ep("============  NEW GAME  ==============")
    q_word=random_word(8)

    word_length = len(q_word)
    az="".join("e, a, t, o, i, n, s, h, r, d, l, u, c, m, f, w, y, g, p, b, v, k, x, j, q, z".upper().split(", "))
    print("AIUEOSBCDFGHJKLMNPQRTVWXYZ",file=sys.stderr)
    ab=az


    print(az,file=sys.stderr)
    # game loop
    words=[]
    states=[]
    no_use_line=[]
    for i in range(word_length):
        no_use_line+=[["#"]]

    use=[]
    hit="*"*word_length
    check_out=[]
    guess=""
    times=0
    while True:
        times+=1
        check_count=0
        #line=[int(i) for i in input().split()]
        line=[int(i) for i in ret_guess(guess).split()]
        if line.count(3)==word_length:
            time_list+=[times]
            break
        if line[0]!=0:
            words+=[guess]
            states+=[line]
        stime=time.time()
        ep("hit,words,states",hit,words,states)
        ep("use",use)
        ep("no_use_line",no_use_line)
        for i,(c,state) in enumerate(zip(guess,line)):
            if state==1:
                ab=ab.replace(c,"")
            if state==3:
                hit=hit[:i]+c+hit[i+1:]
                if c not in use:use+=[c]
            if state==2:
                if c not in use:use+=[c]
                if c not in no_use_line[i]:
                    ep(no_use_line[i])
                    no_use_line[i]+=[c]
                    ep(no_use_line[i])
        hit=last1(hit)
        hit=last1(hit)
        if len(use)==word_length:
            ab="".join(use)
            ep("ab",ab)
        s_use=set(use)
        u_count=0
        answer=[]
        same3=[]
        get_guess(words,states)
        ep("check_count,u_count",check_count,u_count)
        guess=answer[0]
        print(answer[0],"---------------")
print("平均",sum(time_list)/len(time_list))

 


最新の画像もっと見る

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。