Coder Social home page Coder Social logo

battleword's Introduction

Battleword

Wordle is cool right now

What is Wordle

In case you haven't been on the internet over the last few months, Wordle is a word game where you make attempts at guessing a secret word. You use your feeble human brain to think of a word to submit, then the UI will colour each letter according to the Results Key below.

What is Battleword

Battleword is a competition to see who can come up with the fastest/most accurate/shoutiest automatic Wordle solver.

Players host an api somewhere accessible to the public internet. The Battleword engine is running in the cloud and users can send requests to start a Match through the website.

Each word you are trying to guess is one 'Game'. A 'Match' is a collection of several Games against other opponents. All opponents in a Match receive the same set of Games (ie same secret words).

The process of a Battleword match is as follows:

  1. The battleword engine makes a POST request to your API with the state of a Game (starting empty).
  2. Your solver responds to the POST request with its best guess according to the current state of that Game. At this point, the solver has no previous results to use in its calculation, so you shouldn't think too hard here.
  3. The engine responds to your guess by submitting a new POST request to your API, with the results of your guess according to the Results Key below. It will also include all previous guesses of the game. (See /guess for more info).
  4. This is repeated until your solver either guesses the right word, or you run out of guesses (max 6)
  5. Many Games in a Match are run concurrently, your API should be stateless or you'll probably make things harder for yourself than necessary.
  6. Once the all Games in the Match are complete for all players, the engine broadcasts the results of all Games for all players in the Match so you can see how you did compared to others.

Details of the payloads in each step are in the API section below.

Results Key

Battleword maps the colours you know and love in Wordle to the following numbers:

Colour Number Meaning
๐ŸŸฉ 2 Letter is in the word and in the correct spot
๐ŸŸจ 1 Letter is in the word but in a different spot
โฌ› 0 Letter is not in the word

I have coded the Battleword engine to give results in the exact same way as Wordle with respect to double letters, greens over yellows, left to rightishness etc. If you find an inconsistency between how I've done it and the Wordle engine please file an issue.

Quickstart

  1. Download the latest release for your OS and unpack
  2. Run solvo (double click) - this starts solvo the solver. He will listen for game states from engine.
  3. Run engine - this starts sending game states to solvo. With every guess solvo makes, engine will send a new request to solvo with the results of his previous guess. Solvo will ignore those results and choose a completely random word to send next. Your solver should do better than solvo.

There is also a naive solver named howyagoin, named so because he's a bit howyagoin. He uses a fairly simple approach but still does ok. He doesn't use any of the techniques in schwordler that I stole from youtube videos, so it's very possible to do better than howyagoin if you too have access to Youtube or are good at maths.

Setup

To test your own guesser against the engine, create an api that implements the schema below. Once you've done that, run the engine against the api location of your solver like so:

./engine --apis http://localhost:8081

You can specify multiple solvers to compete against each other:

./engine --apis http://localhost:8081,http://localhost:8080

NB these commands are executed in a command line of your choice. Exact syntax may change based on your OS.

API

This is what all solvers need to implement. For each endpoint you will receive the request shown from the game engine, and have to respond with the response shown. If you do not format your response according to what's listed below you will receive an error. Timeout for each request is 300 seconds.

/guess

The engine will hit your api here with the previous results of a game. You are expected to respond with your best guess. Each guess_results object also comes with the start and finish time, plus an ID that correlates to the header guessID that gets sent with each request. I've omitted those fields from this JSON for brevity. Solvo prints out the full body when you run him.

Request:

{
    "game_id": "3bead1b6-cd41-4bd0-9ec0-9b451319efba",
    "guess_results": [
        {
            "guess": "tense",
            "result": [ 1, 0, 0, 0, 0 ]
        },
        {
            "guess": "finer",
            "result": [ 0, 1, 0, 0, 1 ]
        },
        {
            "guess": "unset",
            "result": [ 0, 0, 0, 0, 2 ]
        },
        {
            "guess": "cable",
            "result": [ 0, 0, 0, 0, 0 ]
        },
        {
            "guess": "deity",
            "result": [ 2, 0, 1, 1, 0 ]
        }
    ],
    "guess_durations_ns": [ 1002925700, 1001538400, 1000738000, 1000947200, 1000960600 ]
}

Response:

{
	"guess": "rumba",
	"shout": "why is everybody shouting"
}

Shouts server no purpose except to intimidate your opponents.

/ping

In order to get the definition of your character, the engine will ping you. This is also run at the start of each match up to 10 times in order to wake up your server if you're hosting it in 'serverless' land where there are still servers but I assume the name refers to a lack of reliable servers.

concurrent_connection_limit specifies how many concurrent requests you are happy with. It will also ensure that only concurrent_connection_limit + 2 games are ever active at once for each player so you don't get a scenario where you have to make all 100 first guesses before being sent the second guess. If you are a big dog, you can crank this as high as you like in your ping response.

colour is spelt colour because this is a civilised repo. It is used as the colour for your part of the statistics in the UI. It has to be of the format #FFFFFF (not case sensitive though). If you leave it out I use a hash of your description instead so you'll end up being some random colour you're not happy with.

Request:

GET request - no payload

Response:

{
    "name": "solvo",
    "description": "the magnificent",
    "concurrent_connection_limit": 10,
    "colour": "#7e0391"
}

There will be more things here in the future. stay posted.

/results

Once all players are finished, the engine will send you the results of everyone in the match. No response is required, except maybe to message your friends to brag. player_id represents your ID, look for the corresponding player in the players array to see how you went. As with the request, all objects come with an ID, start, and end time that has been omitted for brevity. Check Solvo for the exact body.

Request:

{
    "player_id": "9fda863c-f303-47da-a8f0-35b0b84b1abe",
    "results": {
        "match_id": "777f785b-d2f9-4467-990e-e2f90efe3b52",
        "players": [
            {
                "player_id": "9fda863c-f303-47da-a8f0-35b0b84b1abe",
                "definition": {
                    "name": "solvo",
                    "description": "the magnificent"
                },
                "games_played": [
                    {
                        "game_id": "3bead1b6-cd41-4bd0-9ec0-9b451319efba",
                        "guess_results": [
                            {
                                "guess": "tense",
                                "result": [ 1, 0, 0, 0, 0 ]
                            },
                            {
                                "guess": "finer",
                                "result": [ 0, 1, 0, 0, 1 ]
                            },
                            {
                                "guess": "unset",
                                "result": [ 0, 0, 0, 0, 2 ]
                            },
                            {
                                "guess": "cable",
                                "result": [ 0, 0, 0, 0, 0 ]
                            },
                            {
                                "guess": "deity",
                                "result": [ 2, 0, 1, 1, 0 ]
                            },
                            {
                                "guess": "deter",
                                "result": [ 2, 0, 1, 0, 1 ]
                            }
                        ],
                        "guess_durations_ns": [ 1002925700, 1001538400, 1000738000, 1000947200, 1000960600, 1001443500 ]
                    }
                ],
            },
            {
                "player_id": "b6f3c0ed-c885-4253-8d23-725def324c55",
                "definition": {
                    "name": "schwordler",
                    "description": "the brave"
                },
                "games_played": [
                    {
                        "game_id": "3bead1b6-cd41-4bd0-9ec0-9b451319efba",
                        "guess_results": [
                            {
                                "guess": "crane",
                                "result": [ 0, 2, 0, 0, 0 ]
                            },
                            {
                                "guess": "droit",
                                "result": [ 2, 2, 2, 2, 2 ]
                            }
                        ],
                        "correct": true,
                        "guess_durations_ns": [ 2135600, 1794700 ]
                    }
                ],
            }
        ],
        "games": [ { "game_id": "3bead1b6-cd41-4bd0-9ec0-9b451319efba", "answer": "droit" } ],
        "rounds_per_game": 6,
        "letters_per_word": 5
    }
}

Competing

Once you have your solver set up, it's time to compete. Head to the Battleword Official Website and onboard your solver here. Paste the full URL of your solver and if it correctly returns a response to the /ping endpoint with a name no one else is using yet, it will add your solver to the list of solvers people can compete against.

Start a game here and watch the solvers go at it. Battleword homepage demo

There is a cornucopia of statistics further down that page, if you didn't get around to building something to analyse your solver's performance yourself.

Tech used

Deploy

  • goreleaser is awesome. Use it for all your releases.
  • Github actions with auto deploy to Firestore
  • Identity Federation meaning roughly key management as a service I guess. Details.

Backend

  • GCP Cloud Run - ok for hosting the services being used (read, completely free and 0 config deploy)
  • GCP is the only cloud that doesn't make me feel like I'm working

Frontend

  • React/Typescript. Don't look at the code I'm not proud of it. An exercise in how fast you can make a frontend.
  • Firestore - bae. In backend and frontend actually. Means I just didn't need an API for the frontend. Always a good time.
  • Firebase hosting - brainless deployment but always surprises me googlists didn't think of a better name for it.

Fun Facts

  • I spent some time investigating but can't figure out why but unmarshalling guesses from solvers takes about 100ms in the cloud when an average laptop takes less than a few nanoseconds. I tried ramping up the compute for a bit to see if it improved but it didn't. Because of this, most solver response time will be dominated by this Serverless Tax and thus you should probably only focus on your accuracy, and making sure your guess takes less than 100ms.
  • Every time I add a new git tag it gets built and released automatically for your consumption. The cloud engine will always be running the latest tag. If you are prototyping against a local version of the engine, take note of which version you are using in case I decide to break the API in the latest tag.

battleword's People

Contributors

brensch avatar crepehat avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

battleword's Issues

summarise games/matches as they arrive

perform the summary as the game is going rather than at the end. this allows clients to get constant snapshots (ie write to firestore at some given refresh interval)

figure out logging in gcp

the error levels aren't coming through. find best way to do logging using logrus for consumption by google logs.

don't run out of sockets

want to be able to run as many games as i like. need a hard limit on concurrent requests to each player and in total

play multiple games

the engine should execute matches which is a series of games.

  • number of games should be definable
  • summary of statistics should be provided in the final result to the solvers

make releasing easier

set up some form of packaging for local binaries. CICD will be a separate task later.

store matches in firestore

should write match state periodically to firestore so that api can look up in firestore. also better for historising games.

one thought is that i could look in the memory of the process that receives the api call (ie have all running matches in a map). If it's there return it immediately (saves a call to firestore and is quicker), if it's not, check firestore. this would still work if i ever want to run the api across multiple instances, and greatly reduce the total number of firestore calls if only a single instance is running.

use logrus

i really should have done this from the start. replace default logger with logrus

create shout relay

players should implement a shout endpoint that we will broadcast shouts to for the lols.

add get user definition endpoint

users should provide an endpoint that they use to define themselves. can use this to set things like concurrent connections they want and their description etc. will also use this to check aliveness of users before matches.

handle client giving an error on a game gracefully

it seems that currently we aren't handling failures correctly. should continue on with all the rest of the games, and just lose that one game. should also then collate the list of errors that were encountered so the player has a chance to understand failures better.

Add guess ID

Can see a scenario where users want to track guesses in logs. This will help that. I am one of those users.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.