兄匟に察しおチェスゲヌムをプログラムした方法





これは、私が兄ず䞀緒にチェスのゲヌムに勝ずうずした方法の話です。たった1぀のク゜ゲヌム。䜕がそんなに特別なのですか私はチェスが埗意ですかどういたしたしお。遊んでいるうちに䜕かを孊びたしたかたた、いいえ。たぶんこれは、目暙ではなく、旅のための旅に぀いおの話ですかあんたり。私もそれを楜しんだのですかわからない。



これは、䞖界で最も研究されおいるゲヌムの1぀で、必芁のない゜フトりェア開発の経隓を䜿甚しお、オリゞナルを目指した私の詊みに぀いおの話です。



私のチェスに察する絶察的な愚かさず、ゲヌムを改善しようずする人々にずっおこの蚘事はたったく圹に立たないにもかかわらず、゚ンゞニアリングの原則を問題に適甚する独自の方法を共有する䟡倀はあったず思いたす。私は成功しおいたすか最埌にこれに぀いお孊びたす。



なぜチェスに関わったのか



2020幎のパンデミックの間、私の兄は他の倚くの人々ず同様に、オンラむンチェスに倢䞭になりたした。数ヶ月プレむした埌、圌はこのゲヌムに぀いお非垞に刺激的に話し始め、他の家族に挑戊し始めたした。私たちの父は電話に出たしたが圌はデゞタルの倧倱敗に芋舞われたしたが、私は諊めたせんでした。制限芁因の1぀は、非垞に時間のかかる可胜性のある趣味に飛び蟌むこずに抵抗を感じたこずです。私はこのゲヌムに぀いお十分に知っおいたので、䞭玚者になるには数千時間ずたではいかなくおも数癟時間を費やす必芁があるこずを理解したした。同時に、私は、圓時すでに100以䞊のゲヌムをプレむしおいた兄に負けるずいう考えにも觊発されなかったこずを認めたす。私は䞀人ではありたせん。



しかしある日、私は圌の挑戊に屈した。蚀うたでもなく、損倱は壊滅的なものでした。子䟛の頃からゲヌムのルヌルや基本を知っおいたしたが、匟のスキルずは比べ物になりたせんでした。埌で、chess.comでゲヌムの分析 を芋るず、私の戊術的なラグは、+ 9のマヌクに達するたで、移動ごずに増加するだけであるこずがわかりたしたこれは、䞍圚に察しおルヌク、ビショップ、ポヌンを倱うこずに等しい敵の損倱の。その瞬間、すべおの垌望を倱っお、私はあきらめたした。これに぀いお䜕かをする必芁があるこずに気付いたずき、同様の状況がさらに2、3のゲヌムで繰り返されたした。



私の最初の決断は、ゲヌムをより深く掘り䞋げるこずでした。



詊み1研究



ゲヌムの品質を改善するための私の最初の詊みは明癜でした。他の孊生からの掚奚事項に぀いおは、RedditずYouTubeにアクセスしおください。GM Naroditskyからのレッスンの 合間に、Lichessの問題を読んで解決する 間、私はむンタヌネット䞊でランダムな察戊盞手ずいく぀かのゲヌムをプレむしたした。これらすべおにもかかわらず、私の評䟡は䜎いたたでした1300〜1400 Rapid on Lichess。







兄ずの詊合をさらに数回行った埌、勝぀チャンスがないこずに気づきたした。私はすべお同じ開発方法挔奏、テクニックの勉匷、ビデオの芖聎に埓い続けたしたが、これに費やす時間は兄よりはるかに少なかったです。圓時、圌はすでに月に数癟のゲヌムをプレむしおいたしたが、私は10歳以䞋でした。このレヌトで、私のギャップはたすたす倧きくなりたした。



その時、私は非垞に重芁なニュアンスに気づきたした。私はゲヌム自䜓には特に興味がなく、実際、ゲヌムを改善したくありたせんでした。私の䞻な目暙は、ただ䞀人の人、぀たり私の兄匟を倒すこずでした。



詊み2敵を研究する



チェスゲヌムは通垞、オヌプニング、 ミドルゲヌム、 ゚ンドゲヌムの3぀のフェヌズに分けられ たす。いく぀かの基本的な亀配パタヌンを孊んだ埌 、ゲヌム終了段階で倧きなアドバンテヌゞから勝利に移行するのは通垞「簡単」なので、そのアドバンテヌゞを埗るこずは私にずっお最初の質問でした。



䞭盀の段階では、通垞、長期的な戊略を展開し、戊術を適甚するこずで 利点が埗られ たす。ゲヌムの原理を読んで勉匷するこずで戊略を改善するこずができ私はそれが奜きです、戊術は問題を解決するこずによっおのみ開発され たす私は特に嫌いです。したがっお、私の兄がchess.comで毎日玄20のそのような問題を解決したこずを考えるず、戊術スキルでは間違いなく遅れるこずを理解したした。私にずっお、これは達成䞍可胜な限界でした。したがっお、残った機䌚は1぀だけでした。それは、オヌプニングステヌゞでアドバンテヌゞを埗るこずです。



オヌプニングフェヌズの背埌にある理論は巚倧です。同時に、長いシヌケンスず動きのバリ゚ヌション、そしお察戊盞手の可胜な答えを蚘憶する必芁がありたす。初心者は倚くを芚える必芁はありたせんが、最も兞型的な開口郚にある皋床粟通しおいるず非垞に有益ですたたはそう蚀われおいたす。



それから私は兄のランダムなゲヌムのいく぀かを芋お、圌が䜿甚した開口郚を理解しようず決心したした。 Lichessのデビュヌに぀いおも勉匷したした 「むタリア党」ず シシリアン・ディフェンスは、圌らの基本原則を思い出そうずしおいる。それずは別に、私はたくさんのYouTubeビデオを芋たした。



明らかに、私の兄は私の前でそしおもっず良くこれをすべおやったので、圓然私は再び負けたした。 少なくずも私にずっおは意味のないオヌプニングの動きを芚えるこずは退屈で疲れるだけであるずいう事実は蚀うたでもありたせん。これはすべお私に決しお喜びを䞎えたせんでした。もう䞀぀の問題は、察戊盞手が本に芏定されおいる動きから逞脱し始めたずき、私は単に新しい䜍眮を理解しおいなかったので、どう反応するかを絶察に知らなかったずいうこずでした。



䞀歩䞋がっお考え盎す時が来たした。それから、私は実際に兄を倒すのではなく、同じオヌプニングを完璧にプレむした察戊盞手ずのゲヌムを改善しようずしおいるこずに気づきたした。もっず方向性を持っお行動できたでしょうか代わりに、圌の兄匟の匱点に察しお特別に準備するこずができたでしょうか明らかに、このアプロヌチは圌に察しおのみ機胜したすが、それは私の目暙ず完党に䞀臎しおいたした。



詊行3プログラミング



今、私の仕事は別の圢をずっおいたす。私の兄匟以䞋、PlayerXが䞍利な立堎にありながら、到達する可胜性が最も高い開口郚の出口の䜍眮を芋぀けるこずです。私たちの誰もがゲヌムの専門家ではなく、私たちのレベルのプレヌダヌはあたり慎重にプレヌしないこずに泚意しおください。



良いプレむダヌに察抗する唯䞀の方法は、本の動きを正確に远うこずです。そうすれば、少なくずも察戊盞手が䜕の動きもせず、アドバンテヌゞを埗るこずができるからです。ただし、クラブレベルのプレヌダヌず察戊しおいる堎合は状況が倉わりたす。敵がこれに正しく反応する可胜性が䜎く、したがっお自分が困難な立堎にあるこずがわかっおいる堎合は、リスクを冒すこずができたす぀たり、䞀時的に䞍利になる。



兄がchess.comでプレむした500以䞊のゲヌムのリストもありたした。そしお、私はプログラマヌなので、この問題を工孊的に解決するこずは私にずっお自然なアプロヌチになりたした。



圌がプレむしたゲヌムをchess.comAPIを䜿甚しおダりンロヌドし、癜ず黒のゲヌムに分け始めたした。それから、癜でプレヌするずきに自分が望む方向にゲヌムを向ける可胜性が高いず感じたので、兄が黒でプレヌするゲヌムに焊点を合わせたした。



import json
import requests

def get_month_games(player, yyyy_mm):
    url = 'https://api.chess.com/pub/player/{}/games/{}'
    r = requests.get(url.format(player, yyyy_mm))
    if not r.ok:
        raise Exception('get_month_games failed')
    games = json.loads(r.content)
    # Format: {games: [{url, pgn}, ...]}
    return games['games']

# ...
      
      





import chess.pgn
import io
import json

with open('games.json') as f:
    data = json.load(f)

games = []
for game in data:
    pgn = io.StringIO(game)
    games.append(chess.pgn.read_game(pgn))

black_games = [g for g in games if g.headers["Black"] == "playerx"]
      
      





次に、タスクを次のように定匏化したした。「PlayerXが芋たすべおのポゞションを考慮するず、オヌプニングの終わりに圌にずっお最も収益性が䜎いず思われるポゞションはどれですか。



今回はタスクが明確に定矩され、身近な分野で䜜業が始たりたした。再利甚可胜なツヌルを䜜成するずいう目暙がなく、利甚可胜なデヌタを調べお1぀の解決策を芋぀けるだけでよいため、Python、぀たりJupyterノヌトブックで分析を行うこずにしたした 。



Pythonには、チェスを操䜜するための優れたラむブラリがすでにあるこずが刀明したした 。python -chess移動の生成、評䟡、芖芚化ず pythonstockfishです。有名なStockfishチェス゚ンゞンを䜿甚しおチェスの䜍眮を評䟡するためのバむンディング。



この方法で問題をグラフに倉換したした。ノヌドは特定のチェスの䜍眮ですFEN衚蚘で説明されおい たす。゚ッゞは2぀のノヌドを接続したすが、タヌゲット䜍眮は蚱容可胜な移動によっお最初のノヌドから到達可胜です。すべおのゲヌムには、1぀の同じ開始ノヌド開始䜍眮がありたす。



次に、PlayerXでプレむされたすべおのゲヌムのグラフを黒ずしお䜜成し、察応する移動が行われた回数で各゚ッゞを远加でマヌクしたした。



class GamesGraph():
    def __init__(self):
        self.graph = igraph.Graph(directed=True)

    def add_move(self, start_fen, end_fen, uci):
        vs = self._ensure_vertex(start_fen)
        vt = self._ensure_vertex(end_fen)
        try:
            e = self.graph.es.find(_source=vs.index, _target=vt.index)
            e["count"] += 1
        except:
            e = self.graph.add_edge(vs, vt)
            e["uci"] = uci
            e["count"] = 1

    @property
    def start_node(self):
        return self.graph.vs.find(chess.STARTING_FEN)

    def _ensure_vertex(self, fen):
        try:
            return self.graph.vs.find(fen)
        except:
            v = self.graph.add_vertex(name=fen)
            v["fen"] = fen
            v["turn"] = chess.Board(fen).turn
            return v
      
      





その結果、次のような重み付き有向グラフ䜍眮は異なる䞀連の移動で取埗できるため、ツリヌではありたせんが埗られたした合成、実際のグラフはここに収たらないため







ここで、開始䜍眮は四角い結び目で瀺され、色はこの䜍眮で移動するのが癜か黒かを瀺したす。



たた、Stockfishを䜿甚したホワむトアドバンテヌゞの芳点から、各ポゞションの評䟡を取埗したいず思いたした。䜕千もの䜍眮を評䟡するプロセスには時間がかかるため、個別に実行しお、䞀意のFEN䜍眮をそれぞれStockfishの掚定倀にマッピングするJSONオブゞェクトを䜜成するこずにしたした。



from stockfish import Stockfish

stock = Stockfish(parameters={"Threads": 8})
stock.set_depth(20)
stock.set_skill_level(20)

def eval_pos(fen):
    stock.set_fen_position(fen)
    return stock.get_evaluation()

# fens -     FEN   .
for fen, node in graph.fens.items():
    node.eva = eval_pos(fen)
      
      





アドバンテヌゞの評䟡は、センチポむントたたは「Xムヌブのチェックメむト」ずしお返されたした 。ここで、正の数は癜のアドバンテヌゞを意味し、黒の負のアドバンテヌゞを意味したす。



{"type":"cp", "value":12}    #    12 .
{"type":"mate", "value":-3}  #      .
      
      





100センチポむントは、1぀のポヌンの察戊盞手よりも有利であるこずを意味し、300は、ビショップのような1぀のマむナヌピヌスを意味したす。ただし、Stockfishは、ピヌスの䜍眮に応じおピヌスに倀を割り圓おるこずに泚意しおください。぀たり、ボヌド䞊のピヌスの数が同じでも、1000センチポむントの利点を埗るこずができる可胜性がありたす。



このスコアを、たずえば0から1たでの数倀など、凊理に䟿利なものにマッピングする必芁がありたした。このため、300以䞊のアドバンテヌゞが1.0で衚瀺され、300以䞊のラグが0で衚瀺されるこずにしたした。 X移動のメむトはXが20であっおも1たたは0になりたす。



#  [-1;1]
def rating(ev, fen):
    val = ev["value"]
    if ev["type"] == "cp":
        #  -300, +300.   .
        val = max(-300, min(300, val))
        return val / 300.0
    #   X :  max .
    if val > 0: return 1.0
    if val < 0: return -1.0
    #   ,     ?
    b = chess.Board(fen)
    return 1.0 if b.turn == chess.WHITE else -1.0

#  [0;1],  0 -  min,  1 -  max   .
def rating_black(ev, fen):
    return -rating(ev, fen) * 0.5 + 0.5
      
      





今ではすべおの情報が適切ではなく、グラフのノヌド぀たり、䜍眮を芋぀ける必芁がありたした。ここでは、黒が負けた䜍眮にあり、それらに到達するための最適な䞀連の動きがありたす。特定の䜍眮に到達する確率を簡単に蚈算できるように、リブの重さを量る必芁がありたした。私はこのように掚論したした



  • 各䜍眮で、察応する゚ッゞに沿ったパスの数をその䜍眮から行われた移動の総数で割るこずにより、特定の移動を行う確率を芋積もるこずができたす。
  • 各゚ッゞの重みは0から1になりたす。倀が倧きいほど、その䜍眮から゚ッゞをトラバヌスする可胜性が高くなりたす。
  • 次に、特定のパスを通過する確率は、通過したすべおの゚ッゞの確率の積になりたす。


暙準のグラフアルゎリズムを䜿甚しお問題を解決するには、次のように゚ッゞの重みを倉換する必芁がありたした。



  • それらは確率ではなく距離を衚しおいたす぀たり、距離が倧きいほど、パスを遞択する可胜性は䜎くなりたす。
  • 2぀のノヌド間の距離は、確率の積ではなく通過した゚ッゞの重みの合蚈でした。


実際、これは説明するよりもはるかに簡単です。匏は非垞に単玔です。



distance(e) = -log(prob(e))
      
      





Pythonでは、次のようになりたす。



def compute_edges_weight(vertex):
    all_count = sum(map(lambda x: x["count"], vertex.out_edges()))
    for edge in vertex.out_edges():
        prob = edge["count"] / all_count
        edge["prob"] = prob
        edge["weight"] = -math.log(prob)
      
      





確率が0から1の間であるため、゚ッゞを遞択する確率の察数を取るず負の数になりたす。確率がれロの堎合を心配する必芁はありたせんその結果、察数はマむナスになりたす。無限倧、グラフの各゚ッゞが少なくずも1回トラバヌスされおいるため... 確率が䜎いほど、察数は負になりたす。぀たり、笊号を逆にするず、必芁なものが埗られたす。理由は次のずおりです。



  • 察数の合蚈は、匕数の積の察数ですlog(a) + log(b) = log(a*b)



    。
  • 結果が倧きいほど、それを決定する確率は䜎くなりたす。






このアむデアを歊噚に、ダむクストラのアルゎリズムを䜿甚しお、開始ノヌドず他のすべおのノヌド間の 最短経路を蚈算でき たす。結果は、各ノヌドず開始䜍眮ぞの最短パスの間のマッピングであり、ノヌドに぀ながる最も可胜性の高い䞀連の移動を衚したす。



この時点で、アドバンテヌゞの最小倀を任意に遞択し、確率でパスを䞊べ替えたした。最初のいく぀かのパスは、PlayerXよりも有利なデビュヌから抜け出すための最倧のチャンスを衚しおいたす。



改善点



私は䜕を知りたしたかこのアルゎリズムによっお䞎えられた䜍眮の䞭には、次のものがありたしたホワむトの動き







ご芧のずおり、黒の最埌の動きであるg6が間違いだったため、黒は非垞に厄介な䜍眮にありたすStockfishによるず+8.9。ホワむトは続けお、e5ずルヌクからポヌンを取りたす。この時点で、黒のゲヌムは実質的に終了したす。圌は階士、h7のポヌン、および叞教を救わなければならないからです。アルゎリズムの別の結果はこれでしたホワむトの動き







ここでは、チェックメむトが1぀の動きで衚瀺されたす 子䟛のチェックメむト。



問題は、PlayerXが最初のゲヌムでこれらの間違いを数回だけ犯し、それを二床ず繰り返さなかったこずです。初期のクむヌンアタックは通垞、経隓の浅いプレむダヌによっおのみ行われ、同じレベルのプレむダヌに察しおのみ効果がありたす。初心者のカテゎリヌを離れたPlayerXは、より有胜な察戊盞手がそのように進たないため、長い間これらの間違いを犯しおいたせん。 PlayerXはそれに察しお防埡する方法を知っおいたので、私はそのような開口郚が機胜しないこずを知っおいたした。



もう1぀の問題は、移動のシヌケンスに関連しおいたした。これは1回だけ発生したしたが、通垞の䜍眮から発生したした。各゚ッゞの確率は1.0であったため、最終的な䜍眮の確率は、最埌の兞型的な䜍眮の確率ず同じであるこずが刀明したした他の可胜性が実行されなかった堎合。以䞋の䟋では、゚ッゞ7ず62番目の動きで最も䞀般的な䜍眮をたどり、次に゚ッゞの1぀を1でたどるこずができたす。さらに、埌続のすべおの手は1回だけ再生されこの䜍眮は1回の詊合でのみ圢成されたため、その結果、各手は1.0の確率になりたす。







そしお、ここに確率がありたす







同じ䞀連の動きが明確に再生される可胜性は䜎いため、このようなスキヌムは信頌できたせん。そのような結論のために、これらの䜍眮からゲヌムが行われる十分なゲヌムがありたせん。



有名な匕甚 B.ブリュヌスタヌ「理論的には理論ず実践の間に違いはありたせんが、実際には違いがありたす」この堎合は正しいこずが刀明したので、より成功した仮説を芋぀けるためにいく぀かの改良ず独立した研究が必芁でしたポゞション。



2番目の問題を修正するために、゚ッゞの確率に䞊限を蚭定しお、1回だけ再生された長い䞀連の動きが埐々に確率を倱うようにしたした。



def compute_edges_weight(vertex, prob_ceiling=0.9):
    all_count = sum(map(lambda x: x["count"], vertex.out_edges()))
    for edge in vertex.out_edges():
        #  ...    (default 90%).
        prob = min(edge["count"] / all_count, prob_ceiling)
        edge["prob"] = prob
        edge["weight"] = -math.log(prob)
      
      





最初の問題では、悪い仮定を手動で陀倖したした。その結果、私は䜜業するポゞションが2、3しか残っおいたせんでした。



別の倉曎の理由は、私がホワむトのためにプレヌし、どのパスを取るかを自分で決めるこずができたので、ホワむトの動きの確率がパスを遞択する確率に圱響を䞎えたくないずいうこずでした。このため、ホワむトの移動のすべおの確率を1.0重みれロに蚭定するず、次のようなグラフになりたす。







準備



勉匷の過皋で、私は次のポゞションを遞びたした







Lichessによるず、これはアレヒンディフェンス2぀のポヌン攻撃です。この䜍眮では、ブラックは1回だけ成功した動きNb6を持っおいたすが、その埌もあたり有利な䜍眮に留たりたせんStockfishによるず+0.6。ただし、この䜍眮から、PlayerXはしばしばNf4を再生したす。これは、圌にずっお非垞に残念なこずです+2.3。Lichessにスタゞオを蚭眮し、いく぀かのバリ゚ヌションPlayerXが再生する良い動きず動きを調べ始めたした。



結局、私は可胜性の朚を手に入れ、それを芚えお理解しようずしたした。たずえば、d5のような動きを脅かすもの、Nf4が倱敗した理由を芋぀け、すべおの人に最適な答えを準備する必芁がありたした。



私はすぐに退屈したので、これを長い間したせんでした、そしお実際、私は郚分的にしか準備ができおいたせんでした。



決定的なゲヌム



それはすべお、私が未来を芋おいるかのように起こりたした。PlayerXず私はアレヒンの防衛の立堎にありたした。䞍快な状況に陥った圌は、5番目の動きで階士を逃したした。あなたよりもはるかに経隓豊富なプレむダヌでさえ、条件を倱うこずに気付いたずきに次々ずミスを犯し始めるこずがわかりたした。勝ったずきははっきりずプレヌするのは簡単ですが、逆の状況で冷静さを保぀こずはできたすか10番手で、私はすでに+7.1のアドバンテヌゞでリヌドしおいたので、負けるのは難しいですが、それは私が考え出した蚈画の終わりでもありたした。黒が今どのように窮屈になっおいるか、そしお私の䜜品がどのように王を攻撃するこずを目指しおいるかを芋おください







その瞬間から、私はあちこちでミスを犯し始めたしたが、同時に私は27を動かすたでいく぀かの利点を維持するこずができたした







残念ながら、時間は非垞に限られおいたので10分間の加速ゲヌムをプレむしたした、すばやく歩かなければなりたせんでした。結局、私は32ず33の動きで臎呜的なミスを犯し、もう䞀床、無敗の察戊盞手からチェックメむトを受け取りたした。







これが党䜓の䞀臎です重倧な゚ラヌなどを含む





ラむブバッチプレビュヌlichess.org/2qKKl2MI



結論



私はこれらすべおから䜕を孊びたしたか振り返っおみるず、そのほずんどが明癜に芋えるいく぀かのこずがありたす。



  1. 特定の察戊盞手の準備をするこずで、オヌプニングの倧きなアドバンテヌゞを埗るこずができたす。
  2. 初心者のプレむダヌは、盞手の疑わしい動きを利甚する機䌚を逃すこずがよくありたす。この点で、クヌデタヌが1぀しかない䜍眮に敵を远いやるこずで、簡単にアドバンテヌゞを埗るこずができたす。
  3. . , . .
  4. , , . .
  5. , , .


私が䜿甚したコヌドはリポゞトリにありたす。私はそこにデヌタを含めおいないこずに泚意しおください、そしおコヌド自䜓はかなりずさんです。それでも、特にコンピュヌタサむ゚ンスの習埗をただ考えおいる人にずっお、これが圹立぀こずを願っおいたす。芋おください-実生掻の問題を解決するのに圹立぀こずでかなり可胜です、それでそれは単にビットを前埌に動かすだけではありたせん。



それがすべおです、友達。い぀か兄を倒せるようになるこずを願っおいたすが、今のずころは自分の力でこれを成し遂げようずしおいたす。








All Articles