Skip to content

New Guide - rock, paper, scissors game for CLUE and CPB. #1185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6f04734
New Guide - rock, player, scissors game for CLUE and CPB.
Jul 22, 2020
e926eaa
Cleaning up comments and some docstrings. Dropping debug level from 3…
Jul 23, 2020
283c980
Audio wav files and image bmp file (sprite sheet) for advanced RPS game.
Jul 23, 2020
0919096
Merge branch 'master' into rockpaperscissors
kevinjwalters Aug 2, 2020
86b7f12
Merge branch 'master' into rockpaperscissors
TheKitty Aug 4, 2020
304aa80
Correcting/improving the error reporting for mulitple missing audio f…
Aug 4, 2020
3806b2f
pylint vs whitespace
Aug 4, 2020
a295bc4
Merge branch 'master' into rockpaperscissors
TheKitty Aug 5, 2020
ea82f2b
Renaming files to a more specific audio_files.
Aug 6, 2020
758280b
Changing evaluateRound() to return value to a form of enumerated valu…
Aug 6, 2020
e3dd2d2
Converting filter() to list comprehension with trailing if filter syn…
Aug 6, 2020
95acc97
Removing the C-esque pad removal in strUnpad() and replacing with rst…
Aug 6, 2020
2612984
Switching to the new match_prefixes for Advertisement matching which …
Aug 6, 2020
ff6fffe
Rearranging the text setting for the summary Label on round results s…
Aug 7, 2020
81fbdd7
Moving points values out to constants.
Aug 8, 2020
a157bbc
Changing assignments of None to _ to normal del for clean-up - this w…
Aug 8, 2020
77554db
Slowing down the bubble sort in showGameResultScreen() a little bit m…
Aug 8, 2020
3db3f47
Merge branch 'master' into rockpaperscissors
TheKitty Aug 10, 2020
2feef1a
Merge branch 'master' into rockpaperscissors
TheKitty Aug 10, 2020
da9216b
Replacing lambda with def per request from code review. #1185
Aug 11, 2020
60b97cc
Merge branch 'rockpaperscissors' of https://github.com/kevinjwalters/…
Aug 11, 2020
ca15fb3
Changing the advertisements used by Advanced RPS Game to have manufac…
Aug 12, 2020
8e8b2f9
Merge branch 'master' into rockpaperscissors
kevinjwalters Aug 12, 2020
fe80b0e
Merge branch 'master' into rockpaperscissors
TheKitty Aug 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
567 changes: 567 additions & 0 deletions CLUE_Rock_Paper_Scissors/advanced/clue-multi-rpsgame.py

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
240 changes: 240 additions & 0 deletions CLUE_Rock_Paper_Scissors/advanced/rps_advertisements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
# MIT License

# Copyright (c) 2020 Kevin J. Walters

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import struct

from adafruit_ble.advertising import Advertisement, LazyObjectField
from adafruit_ble.advertising.standard import ManufacturerData, ManufacturerDataField

# These message should really include version numbers for the
# the protocol and a descriptor for the encryption type

# From adafruit_ble.advertising
# 0xFF is "Manufacturer Specific Data" as per list of types in
# https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/
MANUFACTURING_DATA_ADT = 0xFF
ADAFRUIT_COMPANY_ID = 0x0822

# pylint: disable=line-too-long
# From https://github.com/adafruit/Adafruit_CircuitPython_BLE_BroadcastNet/blob/c6328d5c7edf8a99ff719c3b1798cb4111bab397/adafruit_ble_broadcastnet.py#L84-L85
ADAFRUIT_SEQ_ID = 0x0003

# According to https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/master/adafruit_ble/advertising/adafruit.py
# 0xf000 (to 0xffff) is for range for Adafruit customers

# These four are used as part of prefix matching
RPS_ENC_DATA_ID = 0xfe41
RPS_KEY_DATA_ID = 0xfe42
RPS_ROUND_ID = 0xfe43
GM_JOIN_ID = 0xfe44

RPS_ACK_ID = 0xfe51

# Data formats for shared fields
_DATA_FMT_ROUND = "B"
_DATA_FMT_ACK = "B"
_SEQ_FMT = "B"


class RpsEncDataAdvertisement(Advertisement):
"""An RPS (broadcast) message.
This sends the encrypted choice of the player.
This is not connectable and does not elicit a scan response
based on defaults in Advertisement parent class.
"""
flags = None

_PREFIX_FMT = "<BHBH"
_DATA_FMT_ENC_DATA = "8s"

# match_prefixes tuple replaces deprecated prefix
# comma for 1 element is very important!
match_prefixes = (
struct.pack(
_PREFIX_FMT,
MANUFACTURING_DATA_ADT,
ADAFRUIT_COMPANY_ID,
struct.calcsize("<H" + _DATA_FMT_ENC_DATA),
RPS_ENC_DATA_ID
),
)
manufacturer_data = LazyObjectField(
ManufacturerData,
"manufacturer_data",
advertising_data_type=MANUFACTURING_DATA_ADT,
company_id=ADAFRUIT_COMPANY_ID,
key_encoding="<H"
)

enc_data = ManufacturerDataField(RPS_ENC_DATA_ID, "<" + _DATA_FMT_ENC_DATA)
round_no = ManufacturerDataField(RPS_ROUND_ID, "<" + _DATA_FMT_ROUND)
sequence_number = ManufacturerDataField(ADAFRUIT_SEQ_ID, "<" + _SEQ_FMT)
"""Sequence number of the data. Used in acknowledgements."""
ack = ManufacturerDataField(RPS_ACK_ID, "<" + _DATA_FMT_ACK)
"""Round number starting at 1."""

def __init__(self, *, enc_data=None, round_no=None, sequence_number=None, ack=None):
"""enc_data must be either set here in the constructor or set first."""
super().__init__()
if enc_data is not None:
self.enc_data = enc_data
if round_no is not None:
self.round_no = round_no
if sequence_number is not None:
self.sequence_number = sequence_number
if ack is not None:
self.ack = ack


class RpsKeyDataAdvertisement(Advertisement):
"""An RPS (broadcast) message.
This sends the key to decrypt the previous encrypted choice of the player.
This is not connectable and does not elicit a scan response
based on defaults in Advertisement parent class.
"""
flags = None

_PREFIX_FMT = "<BHBH"
_DATA_FMT_KEY_DATA = "8s"

# match_prefixes tuple replaces deprecated prefix
# comma for 1 element is very important!
match_prefixes = (
struct.pack(
_PREFIX_FMT,
MANUFACTURING_DATA_ADT,
ADAFRUIT_COMPANY_ID,
struct.calcsize("<H" + _DATA_FMT_KEY_DATA),
RPS_KEY_DATA_ID
),
)
manufacturer_data = LazyObjectField(
ManufacturerData,
"manufacturer_data",
advertising_data_type=MANUFACTURING_DATA_ADT,
company_id=ADAFRUIT_COMPANY_ID,
key_encoding="<H"
)

key_data = ManufacturerDataField(RPS_KEY_DATA_ID, "<" + _DATA_FMT_KEY_DATA)
round_no = ManufacturerDataField(RPS_ROUND_ID, "<" + _DATA_FMT_ROUND)
sequence_number = ManufacturerDataField(ADAFRUIT_SEQ_ID, "<" + _SEQ_FMT)
"""Sequence number of the data. Used in acknowledgements."""
ack = ManufacturerDataField(RPS_ACK_ID, "<" + _DATA_FMT_ACK)
"""Round number starting at 1."""

def __init__(self, *, key_data=None, round_no=None, sequence_number=None, ack=None):
"""key_data must be either set here in the constructor or set first."""
super().__init__()
if key_data is not None:
self.key_data = key_data
if round_no is not None:
self.round_no = round_no
if sequence_number is not None:
self.sequence_number = sequence_number
if ack is not None:
self.ack = ack


class RpsRoundEndAdvertisement(Advertisement):
"""An RPS (broadcast) message.
This informs other players the round_no is complete.
An important side-effect is acknowledgement of previous message.
This is not connectable and does not elicit a scan response
based on defaults in Advertisement parent class.
"""
flags = None

_PREFIX_FMT = "<BHBH"

# match_prefixes tuple replaces deprecated prefix
# comma for 1 element is very important!
match_prefixes = (
struct.pack(
_PREFIX_FMT,
MANUFACTURING_DATA_ADT,
ADAFRUIT_COMPANY_ID,
struct.calcsize("<H" + _DATA_FMT_ROUND),
RPS_ROUND_ID
),
)
manufacturer_data = LazyObjectField(
ManufacturerData,
"manufacturer_data",
advertising_data_type=MANUFACTURING_DATA_ADT,
company_id=ADAFRUIT_COMPANY_ID,
key_encoding="<H"
)

round_no = ManufacturerDataField(RPS_ROUND_ID, "<" + _DATA_FMT_ROUND)
sequence_number = ManufacturerDataField(ADAFRUIT_SEQ_ID, "<" + _SEQ_FMT)
"""Sequence number of the data. Used in acknowledgements."""
ack = ManufacturerDataField(RPS_ACK_ID, "<" + _DATA_FMT_ACK)
"""Round number starting at 1."""

def __init__(self, *, round_no=None, sequence_number=None, ack=None):
"""round_no must be either set here in the constructor or set first."""
super().__init__()
if round_no is not None:
self.round_no = round_no
if sequence_number is not None:
self.sequence_number = sequence_number
if ack is not None:
self.ack = ack


class JoinGameAdvertisement(Advertisement):
"""A join game (broadcast) message used as the first message to work out who is playing.
This is not connectable and does not elicit a scan response
based on defaults in Advertisement parent class.
"""
flags = None

_PREFIX_FMT = "<BHBH"
_DATA_FMT = "8s" # this NUL pads for 8s if necessary

# match_prefixes tuple replaces deprecated prefix
# comma for 1 element is very important!
match_prefixes = (
struct.pack(
_PREFIX_FMT,
MANUFACTURING_DATA_ADT,
ADAFRUIT_COMPANY_ID,
struct.calcsize("<H" + _DATA_FMT),
GM_JOIN_ID
),
)
manufacturer_data = LazyObjectField(
ManufacturerData,
"manufacturer_data",
advertising_data_type=MANUFACTURING_DATA_ADT,
company_id=ADAFRUIT_COMPANY_ID,
key_encoding="<H"
)

game = ManufacturerDataField(GM_JOIN_ID, "<" + _DATA_FMT)
"""The name of the game, limited to eight characters."""

def __init__(self, *, game=None):
super().__init__()
if game is not None:
self.game = game
107 changes: 107 additions & 0 deletions CLUE_Rock_Paper_Scissors/advanced/rps_audio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# MIT License

# Copyright (c) 2020 Kevin J. Walters

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from audiocore import WaveFile


class SampleJukeboxError(OSError):
"""Exception raised for any missing audio files.
"""

def __init__(self, files):
self.files = files
super().__init__("Missing audio files: " + ", ".join(files))


class SampleJukebox():
"""This plays wav files and tries to control the timing of memory
allocations within the nRF52840 PWMAudioOut library to minimise
the chance of MemoryError exceptions (2048 bytes)."""

_file_buf = None # Use for WaveFile objects

def _init_wave_files(self, files, directory):
"""Open files from AUDIO_DIR and return a dict with FileIO objects
or None if file not present."""

# 2048 triggers bug in https://github.com/adafruit/circuitpython/issues/3030
self._file_buf = bytearray(512) # DO NOT CHANGE size til #3030 is fixed

missing = []
fhs = {}
for file in files:
wav_file = None
filename = directory + "/" + file + ".wav"
try:
wav_file = open(filename, "rb")
fhs[file] = WaveFile(wav_file, self._file_buf)
except OSError:
# OSError: [Errno 2] No such file/directory: 'filename.ext'
missing.append(filename)

# Raises an exception at the end to allow it to report ALL
# of the missing files in one go to help out the user
if missing:
raise SampleJukeboxError(missing)
self._wave_files = fhs


def __init__(self, audio_device, files,
directory="", error_output=None):
self._audio_device = audio_device
self._error_output = error_output
self._wave_files = None # keep pylint happy
self._init_wave_files(files, directory=directory)

# play a file that exists to get m_alloc called now
# but immediately stop it with pause()
for wave_file in self._wave_files.values():
if wave_file is not None:
self._audio_device.play(wave_file, loop=True)
self._audio_device.pause()
break


def play(self, name, loop=False):
wave_file = self._wave_files.get(name)
if wave_file is None:
return
# This pairing of stop() and play() will cause an m_free
# and immediate m_malloc() which reduces considerably the risk
# of losing the 2048 contiguous bytes needed for this
self._audio_device.stop()
self._audio_device.play(wave_file, loop=loop)
# https://github.com/adafruit/circuitpython/issues/2036
# is a general ticket about efficient audio buffering


def playing(self):
return self._audio_device.playing


def wait(self):
while self._audio_device.playing:
pass


def stop(self):
self._audio_device.pause() # This avoid m_free
Loading