GithubHelp home page GithubHelp logo

kneasle / wheatley Goto Github PK

View Code? Open in Web Editor NEW
15.0 8.0 13.0 573 KB

An AI for Ringing Room that can ring any number of bells to increase the scope of practices.

Home Page: https://pypi.org/project/wheatley/

License: MIT License

Python 100.00%
ringing-room bellringing bot python python3 ai

wheatley's People

Contributors

annag42 avatar centreboard avatar chrisjfield avatar dependabot[bot] avatar jamesscottbrown avatar jrs1061 avatar kneasle avatar tomnatt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

wheatley's Issues

Control start style

In my testing, the bot always used an "up, down, & off" handbell-ish start style, rather than waiting for the Go command (i.e. it started the method a whole-pull after the Treble's Going command took effect).

Perhaps this could be tied to whether the tower is in Tower or Hand mode, or could just be something set from the CLI?

Doesn't wait for cover bell

As they're not part of the generated row, they aren't expected once in the method.
I'd started refactoring to fix this, but will wait for #111 first.

Set at hand should stop current touch

Currently prints upto a row of

ERROR:TOWER:Bell 1 on opposite stroke
ERROR:TOWER:Bell 2 on opposite stroke
ERROR:TOWER:Bell 3 on opposite stroke
ERROR:TOWER:Bell 4 on opposite stroke

and keeps going / waiting

Also there isn't a INFO:TOWER:RECEIVED: line for set at hand

Bell numbering

Currently RowGeneration is done with bells numbered 1 .. n, e.g. [1, 2, 3, 4]
RingingRoom generally refer to bells this way (e.g. on bell assignment or who rang) and it feels much more natural to me to read and reason about.
e.g.

INFO:TOWER:RECEIVED: Assigned bell '1' to 'BOT'
INFO:TOWER:RECEIVED: Assigned bell '2' to 'BOT'

I would read as Treble and Second have been assigned.

The Bot currently uses bells numbered 0 .. n - 1, e.g. [0, 1, 2, 3]. This seems to me like an implementation details of how certain pieces of data are stored and a potential source of off by 1 errors.

Currently the only thing that seems to depend on the bell number being 0 based is RingingRoomTower._bell_state, other usages (e.g. assigned_bells or Rhythm) are just storing whichever bell is passed to them.

I propose exposing

class RingingRoomTower:
    def get_stroke(self, bell: int):
        if bell > len(self._bell_state) or bell <= 0:
            self.logger.error(f"Bell {bell} not in tower")
            return None
        return self._bell_state[bell - 1]

And using 1 based for the bell ints everywhere.

Unintuitive pulloff speed

If Wheatley is given a peal speed and the user is assigned 1 and 2 then we would expect it to still ring at the expected peal speed, regardless of what 1 and 2 do.

Make nicer error messages

Things that cause obscure error messages:

  • Inputting an unknown method name
  • Unknown CompLib comp (causes 404 error, which is kinda OK I guess)
  • Trying to access a private CompLib comp
  • Inputting a tower ID of a tower that doesn't exist
  • Inputting a URL that has one of these issues:
    • Doesn't start with https://
    • Contains a room ID as part of its path
    • Is just straight up invalid
  • Ringing a method on a stage that is 'invalid' given the number of bells in the tower. This produces a not-too-bad error message, but the error is thrown when the method starts, even though that was obviously going to happen. This should probably cause a nice warning before the ringing starts (the ideal scenario IMO is to warn the user in big red letters when the bot starts or the number of bells is changed). The crashing at a method start is probably a decent fail-safe.

I can't think of any more - if anyone thinks of any, make a comment on this issue and I'll add it to this list.

Allow specifying a username for bot

Currently the bot rings all unassigned bells.
Add an optional argument --name that when specified means the bot just rings bells assigned to the given username.

Non goal:
Logging in from the command line + adding the username to the list of users. A user would have to log in with a browser first (and keep the browser open to avoid triggering a leaving event)

Allow Wheatley to speed up after mistakes

I think the best strategy for this is to have Wheatley want to return to the set peal speed - so if people slow down temporarily, Wheatley will gradually return to a normal speed once those datapoints get too old and leave the dataset.

Allow CompLib comps to be accessed from their URLs

Allow accessing of CompLib comps from their URLs as well as IDs (by inserting /rows before the access key to access private compositions). For example:

https://complib.org/composition/66852?accessKey=5d642941de62cd8810189d25df28cca0fad92c07

goes to:

https://complib.org/composition/66852/rows?accessKey=5d642941de62cd8810189d25df28cca0fad92c07

and does indeed load the correct rows.

The CompLib API also works.

Symmetric Place Notation

Currently a place notation block is assumed to be asymmetric unless it starts with &. This is the prefix used in microSIRIL (though that tends to require + for asymmetric parts.)

To match Method XML format specification. we should treat sections as symmetric if there is a ,.
The examples it gives are Cambridge Surprise Minor -36-14-12-36-14-56,12 and Grandsire Doubles 3,1.5.1.5.1.

The specification only allows for at most one ,. On Complib if you try and enter multiple ,s it just ignores the extras on parsing
I.e. -1-1,4.5,-3 becomes -18-18,14.58-38 when displayed / saved. I don't think we need to match behaviour here, it's probably easier not to.

  • Should we continue to allow & to signal a symmetric block?
  • Should we permit + to signal an asymmetric block? e.g. -1-1,+4.5 -> -1-1-1-4.5

Wait while debugging

It would be nice to have a way for Wheatley to ignore time paused under the debugger and continue sensibly rather than start firing all the bells.

Add "Plain Hunt" as a method name

Plain Hunt is not in the CC database, so we should probably make a special case and convert it to Original behind the scenes for the user.

Incorrect expansion of multiple crosses

If the PN parser is given a method that contains multiple xs, if will expand into too many copies of the x. Noticeable for methods like Tulkinghorn Treble Bob Minimus.

Application crash

Attempting to ring Kent TB6. Initially people on 2,3,4,6 with bot on 1,5. Bot rang 1 but crashed on 5 (1st handstroke). Repeated with people on 2,3,4 and bot on 1,5,6; same. Output attached.
wheatley-kent-20201005.txt

Create explicit switch between tower bell mode and handbell mode

Tower bell mode = Pull off in rounds until "go" is called, then ring changes until "that's all is called" and stand when "stand" is called.
Hand bell mode = Up/down/in, stop at first rounds after starting changes.

Possible enhancement would be to have hand bell mode continue if people ring after rounds.

Add fuzzer to test for crashes/hangs in edge cases

Make the bot ring with a mocked 'sleep' function that returns instantly and feed it with randomly perturbed ringing for a while. We don't really care what it ends up doing, so long as it never hangs or gets itself into a loop where it rings really fast.

Requirements when installed from pip

On a clean python install it currently fails:

rr-bot -h
Traceback (most recent call last):
File "c:\python38\lib\runpy.py", line 193, in _run_module_as_main
return _run_code(code, main_globals, None,
File "c:\python38\lib\runpy.py", line 86, in run_code
exec(code, run_globals)
File "C:\Python38\Scripts\rr-bot.exe_main
.py", line 5, in
File "c:\python38\lib\site-packages\rr_bot\main.py", line 11, in
from rr_bot.rhythm import RegressionRhythm, WaitForUserRhythm
File "c:\python38\lib\site-packages\rr_bot\rhythm.py", line 13, in
from rr_bot.regression import calculate_regression
File "c:\python38\lib\site-packages\rr_bot\regression.py", line 5, in
import numpy
ModuleNotFoundError: No module named 'numpy'

I suspect it will need other packages as well

Meta-issue checklist for integration with ringing room

Changes to Wheatley

  • Add subcommand for the server
    • Ring all bells assigned to 'Wheatley' rather than unassigned bells
    • Don't use any settings parameters except tower ID, but instead ask RR for the settings to use
  • Allow settings to be changed whilst Wheatley is running:
    • Changing to a new method
      • Allow calls to be sent over SocketIO
    • Changing to a new complib comp
    • Rhythm settings
  • Make Wheatley return to the main loop if either 'Look To' is set or the 'Stop Touch' signal is sent

Changes to Ringing Room

  • Spawn Wheatley when assigned for the first time
  • Make Wheatley always present in every tower (even if a Wheatley instance isn't running)
  • Add mechanism for broadcasting Wheatley's initial settings
  • Allow fetching and searching of a list of methods valid on a given number of bells
  • Fix bug where Wheatley crashing will fill the RR server log with loads and loads of blank lines
  • Allow storage of Wheatley's settings in a given tower
  • Allow Wheatley to be enabled/disabled in the tower settings
  • Allow the stage of the Method to be stepped to a new stage when tower size changes
  • Abstract all Wheatley functionality into one class
  • Make Wheatley default to tower-bell style not handbell style
  • Add bobs and singles to the default 'plain bob' row gen
  • Make a "Stop Touch" button
  • Make Wheatley send SocketIO signals when stopping and starting ringing
  • Add pretty controls for Wheatley
    • Settings
    • Row Generation
  • Add 'Reset Wheatley' button
  • Kill Wheatley (RIP) if no-one has rung for long enough
  • Make Wheatley spin up when 'Look To' is called
  • Allow Wheatley to be resurrected if he crashes
  • Update Wheatley to recognise users by their IDs not their names
  • Pester Graham John about CORS for complib
  • Put Wheatley in the most bullet-proof configuration possible for launch
  • Write about Wheatley in the 'help' section

Optional?

  • Add Wheatley to host mode
  • Allow collection of stats
  • Recognise CompLib URLs, including private ones

wait_loaded can get into an infinite loop

The main issue is that the SocketIO client can crash in the background, e.g.

Exception in thread Thread-4:
Traceback (most recent call last):
File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
self.run()
File "/usr/lib/python3.8/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "/srv/www/ringingroom/venv/lib/python3.8/site-packages/engineio/client.py", line 574, in _read_loop_polling
self._receive_packet(pkt)
File "/srv/www/ringingroom/venv/lib/python3.8/site-packages/engineio/client.py", line 456, in _receive_packet
self._trigger_event('message', pkt.data, run_async=True)
File "/srv/www/ringingroom/venv/lib/python3.8/site-packages/engineio/client.py", line 494, in _trigger_event
return self.start_background_task(self.handlers[event], *args)
File "/srv/www/ringingroom/venv/lib/python3.8/site-packages/engineio/client.py", line 260, in start_background_task
th.start()
File "/usr/lib/python3.8/threading.py", line 852, in start
_start_new_thread(self._bootstrap, ())
RuntimeError: can't start new thread

This results in a infinite loop, as the client exists and the emit methods don't seem to throw. It just will never receive bell state.

wheatley/wheatley/tower.py

Lines 144 to 156 in 533f617

def wait_loaded(self):
""" Pause the thread until the socket-io connection is open and stable. """
if self._socket_io_client is None:
raise Exception("Not Connected")
iteration = 0
while not self._bell_state:
iteration += 1
if iteration % 50 == 0:
self._join_tower()
self._request_global_state()
sleep(0.1)

Some thoughts

  1. We should put an upper bound on iterations
  2. Currently ever 5 seconds it tries to rejoin the tower and request state, is this worth still doing this and if so is 5 seconds too long?

Custom calls don't work with the 'special' method names

This means you can't ring things like anti-April-Day (Grandsire with Plain Bob calls).

A solution could be that we turn generator_from_special_title into a function that takes a method title and optional calls and always returns a RowGenerator. We should also remove the defaults of 14/1234 calls, and have the generator_from_special_title replacement decide on the custom calls.

Change the cutoff for when calls are processed

Because Go and Stand have two changes warning, there's a possibility that an early call will take place straight away and scupper the ringing. I think the best way to prevent this is to add a slight delay to the call input, so that they appear in the right handstroke/backstroke pair.

Problem with other Tower IDs

I was only able to use the bot successfully in the default Bot Testing Ground tower. When I used my own custom ID (472681395), it didn't seem to connect to the correct tower — it always found 8 bells and never received other commands.

Ringing Room API

As the ringing room api gets added and becomes more stable we should move to using it

Endpoint Method Description
/api/tokens POST Get bearer token
/api/tokens DELETE Revoke bearer token
/api/user GET Get current user details
/api/user POST Register new user
/api/user PUT Modify user settings
/api/user DELETE Delete user account
/api/tower/<tower_id> GET Get connection details for tower_id
  • /api/tower/<tower_id> | GET to get the correct url instead of parsing it off the page
  • /api/tokens | POST to get a user token. This might need to change --name to --email and --password and use /api/user | GET to get the name

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.