Elixir로 NYT Spelling Bee 솔버를 구축한 방법

Wordle은 트위터 피드를 폭풍으로 사로잡았지만, 매일 아침 New York Times Spelling Bee 게임을 하는 것을 좋아합니다.

일련의 문자를 제공하여 작동하며 가능한 한 많은 단어를 생각해 내야 합니다.
  • 제공된 글자만 사용하세요
  • 가운데 문자를 한 번 이상 사용하십시오
  • 4자 이상이어야 합니다
  • .

    게임은 다음과 같습니다.

    Screenshot of a game of NYT Spelling Bee

    최근에 나는 Elixir를 사용하여 이 게임을 위한 솔버를 구축하는 일에 착수했습니다. 대부분의 코드는 이 게시물에 있지만 전체 프로젝트를 보려면 check it out on GitHub 볼 수 있습니다.

    성공의 정의



    Spelling Bee의 단어는 선별되어 의도적으로 모호한 단어를 제외하기 때문에 이 프로그램에서 솔루션에 포함되지 않은 단어를 제안할 것으로 예상해야 합니다.

    따라서 공식 솔루션의 일부가 아닌 제안된 단어를 무시하고 식별된 솔루션 단어의 비율로 이 프로그램의 성공을 판단하겠습니다.

    단어 목록 가져오기



    내 접근 방식은 매우 간단했습니다. 영어 단어 목록으로 시작한 다음 게임 기준에 따라 필터링해야 했습니다.

    영어 단어 목록을 얻으려면 word_list package that I created 을 사용했습니다.

    프로젝트 시작



    새 프로젝트를 만든 후 word_listdeps 배열에 추가하여 mix.exs 패키지를 가져왔습니다.

    defp deps do
      [
        {:word_list, "~> 0.1.0"}
      ]
    end
    


    그런 다음 mix deps.get를 실행하여 종속성을 설치했습니다.

    테스트 주도 개발



    다음으로, 몇 가지 기본 기능을 정의할 수 있도록 몇 가지 기본 테스트를 작성했습니다.

    defmodule SpellingBeeSolverTest do
      use ExUnit.Case
      doctest SpellingBeeSolver
    
      test "finds some valid words" do
        word_stream = SpellingBeeSolver.solve("t", ["m","y","r","i","f","o"])
    
        assert "mortify" in word_stream
        assert "fifty" in word_stream
      end
    
      test "all words include center letter" do
        word_stream = SpellingBeeSolver.solve("t", ["m","y","r","i","f","o"])
    
        assert Enum.all?(word_stream, fn word -> String.contains?(word, "t") end)
      end
    end
    


    이 테스트는 최소한 일부 유효한 단어가 있는지, 모든 단어에 가운데 문자가 포함되어 있는지 확인합니다.

    코드 작성


    solve/2 함수는 다음과 같은 접근 방식을 취합니다.
  • 영단어 스트림을 가져옵니다
  • 단어는 4자 이상이어야 하므로 길이가 3보다 큰 필터를 적용합니다.
  • 단어에 가운데 문자가 포함되어 있는지 확인하는 필터 적용
  • 단어의 모든 문자가 가장자리 또는 중앙 문자에 있는지 확인하는 필터 적용

  • 다음은 lib/spelling_bee_solver.ex 파일의 내용입니다.

    defmodule SpellingBeeSolver do
      def solve(center, edges) do
        WordList.getStream!()
        |> Stream.filter(fn word -> String.length(word) > 3 end)
        |> Stream.filter(fn word -> String.contains?(word, center) end)
        |> Stream.filter(fn word ->
          String.split(word, "", trim: true)
          |> Enum.all?(fn letter -> letter in edges ++ [center] end)
        end)
      end
    
      def printSolution(center, edges) do
        solve(center, edges)
        |> Enum.each(fn x -> IO.puts(x) end)
      end
    end
    


    솔루션을 인쇄하려면 대신 입력을 printSolution/2로 전달할 수 있습니다.

    하지만 효과가 있습니까?



    나는 3일 동안의 솔루션에 대해 내 프로그램을 테스트했고 내 프로그램은 매번 모든 솔루션을 찾았습니다.

    성공! ✅

    매번 몇 개의 추가 단어가 발견되었지만 솔루션은 선별된 단어 세트이기 때문에 예상할 수 있습니다.

    개선의 여지



    내가 할 수 있는 일이 더 많다고 확신하지만 다음은 개선의 여지가 있다고 생각하는 몇 가지 영역입니다.
  • 문자열 배열 대신 가장자리에 문자열을 전달하는 것이 더 편리할 것입니다.
  • 입력 유효성 검사를 수행하지 않았으므로 입력이 올바른 형식으로 제공되지 않으면 예기치 않은 동작이 발생할 수 있습니다
  • 현재 프로그램이 작동하려면 iex에서 실행하거나 다른 Elixir 프로젝트로 가져와야 합니다. 이 논리를 cli 도구나 웹 인터페이스에 넣는 것이 더 실용적입니다.
  • 이것이 더 큰 프로젝트의 일부로 포함된 경우 앞으로 표시되는 것을 피하기 위해 이전 솔루션의 일부가 아닌 단어를 추적하는 것이 좋습니다.

  • 더 많은 콘텐츠



    이 글이 마음에 드셨다면 제 다른 글들도 마음에 드실 겁니다. 내 새 게시물에 대한 알림을 받고 싶거나 내 brief monthly newsletter를 구독하십시오.
  • Reading the Elixir source code to learn how if blocks work
  • I published my first Elixir package to hex!


  • The weird quirk of JavaScript arrays (that you should never use)
  • Does Elixir have for loops?
  • Learn Elixir with me!
  • Project Tours: Bread Ratio Calculator
  • Changing Emoji Skin Tones Programmatically

  • 5 tips for publishing your first npm package
  • 4 Hugo Beginner Mistakes
  • 좋은 웹페이지 즐겨찾기