implemented Scoundrel game
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
T-DeckARD is a curated, interconnected and integrated collection of Python 3 modules developed for CircuitPython 10.x on LilyGO T-Deck (but also mostly compatible with MicroPython and desktop CPython) aimed at turning the stock (Circuit)Python REPL into an easy to use operating environment.
|
T-DeckARD is a curated, interconnected and integrated collection of Python 3 modules developed for CircuitPython 10.x on LilyGO T-Deck (but also mostly compatible with MicroPython and desktop CPython) aimed at turning the stock (Circuit)Python REPL into an easy to use operating environment.
|
||||||
|
|
||||||
T-DeckARD modules are divided into two categories: **components**, which are the building blocks anyone can use to easily create their own applications, and ready-made **applets** which can already be used as stand-alone applications, as well as included as components of other, more complicated ones.
|
T-DeckARD modules are divided into two categories: **components**, which are the building blocks anyone can use to easily create their own applications, and ready-made **applets** which can already be used as stand-alone applications, as well as included as components of other, more complicated ones. This repo also hosts the **games** section which is just a special kind of applets that implement various computer games.
|
||||||
|
|
||||||
## Prerequisites for T-Deck (Plus)
|
## Prerequisites for T-Deck (Plus)
|
||||||
|
|
||||||
@@ -339,6 +339,20 @@ Exports the following functions:
|
|||||||
- `bloged()`: same as `blogpost()` but with a full-featured `app.ed` interface
|
- `bloged()`: same as `blogpost()` but with a full-featured `app.ed` interface
|
||||||
- `blogpost_str(content)`: post the content in a non-interactive manner
|
- `blogpost_str(content)`: post the content in a non-interactive manner
|
||||||
|
|
||||||
|
## Games (`game.*`)
|
||||||
|
|
||||||
|
As of now, T-DeckARD implements the following games.
|
||||||
|
|
||||||
|
### Scoundrel (`game.scoundrel`)
|
||||||
|
|
||||||
|
A port of the rogue-like card game Scoundrel to CircuitPython. Uses `M` to denote monsters, `W` to denote weapons, `P` to denote potions and `D` to denote weapon durability.
|
||||||
|
|
||||||
|
Implements the [official ruleset](http://www.stfj.net/art/2011/Scoundrel.pdf) of the game with no modifications.
|
||||||
|
|
||||||
|
Run: `scoundrel()` from the REPL itself, or use `from game.scoundrel import scoundrel` in other code.
|
||||||
|
|
||||||
|
Controls: enter `a`, `b`, `c` or `d` to interact with an item above this letter in the room, or `r` to run from the room in case it's possible. To quit the game before winning or losing, enter the `q` command at any time.
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
### Is this an OS shell?
|
### Is this an OS shell?
|
||||||
|
|||||||
+14
-2
@@ -16,6 +16,20 @@ _idx_map = {
|
|||||||
'0': 9, 'p': 9
|
'0': 9, 'p': 9
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def confirm(msg, yes='y', no='n', alt_prompt=None):
|
||||||
|
prompt=f' ({yes}/{no}) '
|
||||||
|
yes = yes.lower()
|
||||||
|
no = no.lower()
|
||||||
|
if alt_prompt is not None:
|
||||||
|
prompt = alt_prompt
|
||||||
|
final_prompt = msg + prompt
|
||||||
|
while True:
|
||||||
|
choice = input(final_prompt).strip().lower()
|
||||||
|
if choice == yes:
|
||||||
|
return True
|
||||||
|
elif choice == no:
|
||||||
|
return False
|
||||||
|
|
||||||
def menu(choices, max_size = 10):
|
def menu(choices, max_size = 10):
|
||||||
"""
|
"""
|
||||||
Presents a menu of choices and prompts for numeric or
|
Presents a menu of choices and prompts for numeric or
|
||||||
@@ -50,5 +64,3 @@ def menu(choices, max_size = 10):
|
|||||||
else:
|
else:
|
||||||
print('Invalid choice!')
|
print('Invalid choice!')
|
||||||
return menu(choices)
|
return menu(choices)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
# Scoundrel card roguelike game port to T-DeckARD
|
||||||
|
# Created by Luxferre in 2026, released into public domain
|
||||||
|
|
||||||
|
from deck.input import input
|
||||||
|
from deck.menu import confirm
|
||||||
|
|
||||||
|
try:
|
||||||
|
from random import shuffle
|
||||||
|
except:
|
||||||
|
from random import randint
|
||||||
|
def shuffle(items):
|
||||||
|
for i in range(len(items) - 1, 0, -1):
|
||||||
|
j = randint(0, i)
|
||||||
|
items[i], items[j] = items[j], items[i]
|
||||||
|
|
||||||
|
def scoundrel():
|
||||||
|
# prepare the deck: M02 to M14 (2x), W02 to W10, P02 to P10
|
||||||
|
deck = []
|
||||||
|
for i in range(2, 11):
|
||||||
|
idx = f'{i:02}'
|
||||||
|
deck.append('M'+idx)
|
||||||
|
deck.append('M'+idx)
|
||||||
|
deck.append('W'+idx)
|
||||||
|
deck.append('P'+idx)
|
||||||
|
for i in range(11, 15):
|
||||||
|
idx = f'{i:02}'
|
||||||
|
deck.append('M'+idx)
|
||||||
|
deck.append('M'+idx)
|
||||||
|
shuffle(deck)
|
||||||
|
|
||||||
|
# init main stats
|
||||||
|
hp = 20
|
||||||
|
weapon = 0
|
||||||
|
durability = 0
|
||||||
|
can_run = True
|
||||||
|
engaged = 0
|
||||||
|
potion_drank = False
|
||||||
|
last_potion_val = 0 # for final full-HP win scoring
|
||||||
|
|
||||||
|
# room holder
|
||||||
|
room = []
|
||||||
|
|
||||||
|
print('===============')
|
||||||
|
print('Scoundrel--2026')
|
||||||
|
print(' by Luxferre')
|
||||||
|
print('===============')
|
||||||
|
print(' Good luck!')
|
||||||
|
|
||||||
|
while hp > 0 and len(deck) > 0: # start main game loop
|
||||||
|
# get current room
|
||||||
|
if engaged == 0:
|
||||||
|
while len(room) < 4:
|
||||||
|
try:
|
||||||
|
val = deck.pop()
|
||||||
|
if val is not None:
|
||||||
|
room.append(val)
|
||||||
|
except: # reached the end of the deck
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
can_run = False
|
||||||
|
# print your stats
|
||||||
|
print(f'---------------\nHP:{hp:02} W:{weapon:02} D:{durability:02}\n---------------')
|
||||||
|
# print the current room state
|
||||||
|
print(' '.join(room))
|
||||||
|
valid_choices='abcdrq'
|
||||||
|
if can_run:
|
||||||
|
print(' a b c d r)un->')
|
||||||
|
else:
|
||||||
|
print(' a b c d')
|
||||||
|
valid_choices='abcdq'
|
||||||
|
choice = ''
|
||||||
|
while choice not in(valid_choices) or choice == '':
|
||||||
|
choice = input('Your choice: ').strip().lower()
|
||||||
|
item = ' '
|
||||||
|
if choice == 'q':
|
||||||
|
print('Quitting the game...')
|
||||||
|
return 0
|
||||||
|
elif choice == 'r' and can_run:
|
||||||
|
shuffle(room)
|
||||||
|
for el in room:
|
||||||
|
deck.insert(0, el)
|
||||||
|
room = []
|
||||||
|
can_run = False
|
||||||
|
continue
|
||||||
|
elif choice == 'a':
|
||||||
|
item = room[0]
|
||||||
|
room[0] = ' '
|
||||||
|
elif choice == 'b':
|
||||||
|
item = room[1]
|
||||||
|
room[1] = ' '
|
||||||
|
elif choice == 'c':
|
||||||
|
item = room[2]
|
||||||
|
room[2] = ' '
|
||||||
|
elif choice == 'd':
|
||||||
|
item = room[3]
|
||||||
|
room[3] = ' '
|
||||||
|
if item == ' ':
|
||||||
|
print('Invalid item choice!')
|
||||||
|
continue
|
||||||
|
itemtype = item[0].upper()
|
||||||
|
itemval = int(item[1:])
|
||||||
|
if itemtype == 'M': # monster
|
||||||
|
engaged += 1
|
||||||
|
healthlost = itemval
|
||||||
|
print(f'You fight a {itemval}-level monster.')
|
||||||
|
if durability >= itemval:
|
||||||
|
print(f'You\'ll lose {itemval} HP if fighting with bare hands.')
|
||||||
|
print(f'You\'ll lose {max(itemval-weapon, 0)} HP if using your weapon.')
|
||||||
|
if confirm(f'Use weapon?'):
|
||||||
|
healthlost -= weapon
|
||||||
|
if healthlost < 0: healthlost = 0
|
||||||
|
durability = itemval - 1
|
||||||
|
if durability < 2: # lose weapon when slaying the weakest monster
|
||||||
|
print('You lose your weapon.')
|
||||||
|
durability = 0
|
||||||
|
weapon = 0
|
||||||
|
hp -= healthlost
|
||||||
|
if hp < 0: hp = 0 # GGWP
|
||||||
|
print(f'You lost {healthlost} HP. New HP: {hp}.')
|
||||||
|
if weapon > 0:
|
||||||
|
print(f'Your weapon\'s durability decreased to {durability}.')
|
||||||
|
elif itemtype == 'W': # equip a weapon
|
||||||
|
engaged += 1
|
||||||
|
weapon = itemval
|
||||||
|
print(f'You equip a {weapon}-level weapon.')
|
||||||
|
durability = 14
|
||||||
|
elif itemtype == 'P': # drink a potion
|
||||||
|
engaged += 1
|
||||||
|
if not potion_drank:
|
||||||
|
potion_drank = True
|
||||||
|
hp += itemval
|
||||||
|
if hp > 20: hp = 20
|
||||||
|
print(f'You drink a {itemval}-level potion. New HP: {hp}')
|
||||||
|
if len(deck) == 0:
|
||||||
|
last_potion_val = itemval
|
||||||
|
else:
|
||||||
|
print('You already drank a potion in this room. Discarding.')
|
||||||
|
else: # we shouldn't get here but...
|
||||||
|
print('Invalid item choice!')
|
||||||
|
continue
|
||||||
|
if hp > 0 and engaged == 3: # used three items in a room, moving on to next
|
||||||
|
print('Room clear, moving to the next...')
|
||||||
|
engaged = 0
|
||||||
|
potion_drank = False
|
||||||
|
can_run = True
|
||||||
|
room = list(filter(lambda s: s!=' ', room))
|
||||||
|
# score the result
|
||||||
|
score = 0
|
||||||
|
if hp > 0: # win
|
||||||
|
score = hp
|
||||||
|
if hp == 20:
|
||||||
|
score += last_potion_val
|
||||||
|
else: # fail
|
||||||
|
for el in room:
|
||||||
|
deck.insert(0, el)
|
||||||
|
for item in deck:
|
||||||
|
itemtype = item[0].upper()
|
||||||
|
itemval = int(item[1:])
|
||||||
|
if itemtype == 'M':
|
||||||
|
score -= itemval
|
||||||
|
if score > 0:
|
||||||
|
print('You won! Your score: ' + score)
|
||||||
|
else:
|
||||||
|
print('You lost! Your score: ' + score)
|
||||||
|
return score
|
||||||
|
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ from app.ed import edit, view, edbuf, viewbuf, edurl, viewurl
|
|||||||
from app.bro import bro
|
from app.bro import bro
|
||||||
from app.blog import blogpost, blogpost_str, bloged
|
from app.blog import blogpost, blogpost_str, bloged
|
||||||
from app.llmchat import llmchat
|
from app.llmchat import llmchat
|
||||||
|
from game.scoundrel import scoundrel
|
||||||
try:
|
try:
|
||||||
from deck import net
|
from deck import net
|
||||||
requests = net.init_requests()
|
requests = net.init_requests()
|
||||||
|
|||||||
Reference in New Issue
Block a user