Brythonで書く神経衰弱ゲーム – ブラウザ上で動作するPython3実装

    ブラウザ上で動作する言語と聞いて真っ先に思い浮かぶのはJavaScriptだと思います。
    でも、BrythonというJavaScriptで書かれたライブラリを利用すれば、Pythonでも動かせるんです!
    しかも文法はPython3です。そして標準ライブラリのほとんどが使えます。動作は軽快です。コールバックの設定にデコレータを使えて視覚的にも気分的にもスッキリできます。
    日本語の資料が少ないのが弱点ですが、積極的に情報発信してもっと増やしていきたいものですね。

    ゲーム制作に際し、フリーアイコン素材サイトICOOON MONOさんの画像を使わせていただきました。いつも高品位なアイコン素材ありがとうございます。

    神経衰弱ゲームのコード解説

    jQueryをBrythonから使っています。
    pythonの文法上$は識別子として使えませんので、jq=window.jQueryとしてjqを$の代わりに使っています。

    気持ちいいのは、デコレータでコールバックの登録ができるところですね。

    @bind(jq('#item li'), 'mousedown')
    def onSelect(ev):
    

    以下、コードの全容です。(2文字インデントで少し読みにくいですが…)
    ※ マウス押下時の判定に不備がありましたので修正しました(2019/03/18)

    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script type="text/javascript" src="https://cdn.rawgit.com/brython-dev/brython/master/www/src/brython.js"></script>
    <script type="text/javascript" src="https://cdn.rawgit.com/brython-dev/brython/master/www/src/brython_stdlib.js"></script>
    <script type="text/python">
    from browser import window,bind,timer,alert
    from random import shuffle
    
    jq = window.jQuery
    n_kind = 8
    indexes = list(range(1,n_kind+1))
    indexes += indexes
    selected, cleared = None,None
    ignore_select = True
    
    for i in range(2*n_kind):
      jq("#item").append("<li><img src='/wp/wp-content/uploads/2019/03/icon-128px-hatena.jpeg'></li>");
    
    def reset():
      shuffle(indexes)
      global cleared,selected,ignore_select
      cleared,selected = [],[]
      ignore_select = False
      for i in range(2*n_kind):
        close(i)
    
    @bind(jq('#item li'), 'mousedown')
    def onSelect(ev):
      global selected
      if ignore_select: return
      idx = jq('#item li').index(ev.currentTarget)
      if len(selected) < 2 and idx not in selected and idx not in cleared:
        selected.append(idx)
        open(idx)
        if len(selected) == 2:
          check()
    
    def open(idx):
      img_num = indexes[idx]
      jq(f'#item li:eq({idx}) img').attr('src',f'/wp/wp-content/uploads/2019/03/icon-128px-{img_num}.jpeg')
    
    def close(idx):
      jq(f'#item li:eq({idx}) img').attr('src','/wp/wp-content/uploads/2019/03/icon-128px-hatena.jpeg')
    
    def close_selected_items():
      global selected,ignore_select
      close(selected[0])
      close(selected[1])
      selected = []
      ignore_select = False
    
    def splash():
      global ignore_select
      ignore_select = True
      cnt = 10
      def f():
        nonlocal cnt
        shuffle(indexes)
        if cnt > 0:
          for i in range(2*n_kind):
            (open if cnt%2 else close)(i)
          cnt -= 1
          timer.set_timeout(f,300)
        else:
          reset()
          
      return f
    
    def check():
      global selected, cleared
      if indexes[selected[0]] == indexes[selected[1]]:
        cleared.extend(selected)
        selected = []
        if len(cleared) == 2*n_kind:
          timer.set_timeout(splash(),250)
      else:
        ignore_select = True
        timer.set_timeout(close_selected_items, 500)
    
    splash()()
    
    </script>