Compare commits
124 commits
update_boo
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
508a0f5b87 | ||
|
|
c24e40fd3f | ||
|
|
d96ee58a58 | ||
|
|
a2253d6d5c | ||
|
|
13f6751504 | ||
|
|
bbbe417f6f | ||
|
|
f9ce279198 | ||
|
|
98838420a9 | ||
|
|
c2307ce9bc | ||
|
|
88cb051636 | ||
|
|
52e0aedf2e | ||
|
|
042bbc22ad | ||
|
|
6e06ed1a43 | ||
|
|
28cdd13f5c | ||
|
|
4d38040567 | ||
|
|
79c43b7416 | ||
|
|
22715b739f | ||
|
|
c47c06865d | ||
|
|
7f9a66b5c4 | ||
|
|
28aba35b8d | ||
|
|
39abae5d6c | ||
|
|
6d5634aed3 | ||
|
|
bc2ce6a2d3 | ||
|
|
478b550a17 | ||
|
|
631d270860 | ||
|
|
aa5ed36c5f | ||
|
|
e53cd2e089 | ||
|
|
091b1934d8 | ||
|
|
9bfcc84723 | ||
|
|
a69d6c878a | ||
|
|
4cd27e78b6 | ||
|
|
876e5e18d6 | ||
|
|
0f12a36ff6 | ||
|
|
7b98eec44c | ||
|
|
6ff675b58d | ||
|
|
8f330e4caf | ||
|
|
60cd407fff | ||
|
|
ebbd201a3a | ||
|
|
2de417326c | ||
|
|
4c561b5911 | ||
|
|
6b8b3b46f8 | ||
|
|
9bcfe9f97c | ||
|
|
8b5b975ed6 | ||
|
|
fe54703f22 | ||
|
|
8dc9ab5fbe | ||
|
|
c9335715b8 | ||
|
|
f3e9e23c47 | ||
|
|
42bb023cef | ||
|
|
235ed6018b | ||
|
|
cfe93619d0 | ||
|
|
d7302ea2a7 | ||
|
|
671486221c | ||
|
|
7fe7b0d788 | ||
|
|
ec382f3c51 | ||
|
|
e457cde29f | ||
|
|
85a95a8203 | ||
|
|
c860ebf833 | ||
|
|
2b852f7b55 | ||
|
|
34d3d7eb3a | ||
|
|
849c272ac0 | ||
|
|
e9ad6d3c57 | ||
|
|
cec438572f | ||
|
|
d2f4c21809 | ||
|
|
40a26666d2 | ||
|
|
205d655dc4 | ||
|
|
6d1665b6ee | ||
|
|
675143d56d | ||
|
|
49ecec7b14 | ||
|
|
9f58e6b912 | ||
|
|
4c629abbee | ||
|
|
ecda53b0cf | ||
|
|
fea27725f7 | ||
|
|
96fe7ddc7b | ||
|
|
d58747960f | ||
|
|
469fc2a1b9 | ||
|
|
16a50d8a4d | ||
|
|
b54c7e0be7 | ||
|
|
1f776f3b4d | ||
|
|
eee9a86c08 | ||
|
|
8679a4dafa | ||
|
|
7efd046d44 | ||
|
|
160edc8d61 | ||
|
|
4151d3b80c | ||
|
|
0699a56feb | ||
|
|
36626af858 | ||
|
|
8b42a9ae4d | ||
|
|
703c97e7ee | ||
|
|
33b636a49b | ||
|
|
d80e33d35b | ||
|
|
fe83c858b7 | ||
|
|
744ed7ed32 | ||
|
|
68f6335f3b | ||
|
|
5472486bf4 | ||
|
|
44b571a9e3 | ||
|
|
37b4479f28 | ||
|
|
9eb8b04c9c | ||
|
|
df7f64f059 | ||
|
|
486d433a0b | ||
|
|
1da2230527 | ||
|
|
7480c1f4eb | ||
|
|
37c278ab63 | ||
|
|
228f25cbb7 | ||
|
|
e540625421 | ||
|
|
50b444fd70 | ||
|
|
37b218c28d | ||
|
|
ca8cb2d26a | ||
|
|
11921ae2e1 | ||
|
|
ae580c46db | ||
|
|
6121d2708c | ||
|
|
b6f76729cd | ||
|
|
fb9c1614d3 | ||
|
|
e49c754ebb | ||
|
|
45bd92db14 | ||
|
|
dfe3eec912 | ||
|
|
e41c7e56c4 | ||
|
|
b1930c7231 | ||
|
|
b9f9f8e1b8 | ||
|
|
5dcf983bb4 | ||
|
|
a5e9c5a38e | ||
|
|
5775bc25f2 | ||
|
|
cec3533a3a | ||
|
|
d2d52558e5 | ||
|
|
3b639be9dd | ||
|
|
654157510b |
33
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# SPDX-FileCopyrightText: 2025 Tim Cocks, written for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
name: Validate Build
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
validate-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up requested Python version
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
- name: Versions
|
||||
shell: bash
|
||||
run: |
|
||||
python3 --version
|
||||
- name: Checkout Current Repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
show-progress: false
|
||||
- name: Install reqs
|
||||
shell: bash
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
- name: Build Fruit Jam OS
|
||||
shell: bash
|
||||
run: |
|
||||
python build.py
|
||||
76
.github/workflows/daily_release_check.yml
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# SPDX-FileCopyrightText: 2025 Tim Cocks, written for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
name: Daily Release Check
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: 15 8 * * *
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release_check:
|
||||
if: github.repository_owner == 'adafruit'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
release_created: ${{ steps.check_release.outputs.release_created }}
|
||||
assets_upload_url: ${{ steps.check_release.outputs.assets_upload_url }}
|
||||
steps:
|
||||
- name: Set up requested Python version
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
- name: Versions
|
||||
shell: bash
|
||||
run: |
|
||||
python3 --version
|
||||
- name: Checkout Current Repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
show-progress: false
|
||||
- name: Install reqs
|
||||
shell: bash
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
- name: Create Release If Needed
|
||||
id: check_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
python release_updater.py make_release
|
||||
|
||||
upload_release_assets:
|
||||
needs: release_check
|
||||
if: github.repository_owner == 'adafruit' && needs.release_check.outputs.release_created == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up requested Python version
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
- name: Versions
|
||||
shell: bash
|
||||
run: |
|
||||
python3 --version
|
||||
- name: Checkout Current Repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
show-progress: false
|
||||
- name: Install reqs
|
||||
shell: bash
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
- name: Build assets
|
||||
shell: bash
|
||||
run: |
|
||||
python build.py
|
||||
- name: Upload Release Assets
|
||||
uses: shogo82148/actions-upload-release-asset@v1
|
||||
with:
|
||||
asset_path: "dist/*"
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
upload_url: ${{ needs.release_check.outputs.assets_upload_url }}
|
||||
42
.github/workflows/release_gh.yml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# SPDX-FileCopyrightText: 2025 Tim Cocks, written for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
name: GitHub Release Actions
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
upload-release-assets:
|
||||
if: github.repository_owner == 'adafruit'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up requested Python version
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
- name: Versions
|
||||
shell: bash
|
||||
run: |
|
||||
python3 --version
|
||||
- name: Checkout Current Repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
show-progress: false
|
||||
- name: Install reqs
|
||||
shell: bash
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
- name: Build assets
|
||||
shell: bash
|
||||
run: |
|
||||
python build.py
|
||||
- name: Upload Release Assets
|
||||
uses: shogo82148/actions-upload-release-asset@v1
|
||||
with:
|
||||
asset_path: "dist/*"
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
3
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
dist
|
||||
|
||||
latest_dl
|
||||
Adafruit_Learning_System_Guides
|
||||
|
|
|
|||
1
README.rst
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Fruit Jam OS
|
||||
109
build.py
|
|
@ -1,7 +1,26 @@
|
|||
from datetime import datetime
|
||||
import os
|
||||
import zipfile
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from circup.commands import main as circup_cli
|
||||
|
||||
|
||||
# each path is a tuple that contains:
|
||||
# (path within learn repo, directory name to use inside of apps/)
|
||||
LEARN_PROJECT_PATHS = [
|
||||
("Metro/Metro_RP2350_Snake/","Metro_RP2350_Snake"),
|
||||
("Metro/Metro_RP2350_Memory/memory_game/", "Metro_RP2350_Memory"),
|
||||
("Metro/Metro_RP2350_CircuitPython_Matrix/", "Metro_RP2350_CircuitPython_Matrix"),
|
||||
("Metro/Metro_RP2350_FlappyNyanCat/", "Metro_RP2350_FlappyNyanCat"),
|
||||
("Metro/Metro_RP2350_Match3/match3_game/", "Metro_RP2350_Match3"),
|
||||
("Metro/Metro_RP2350_Breakout/", "Metro_RP2350_Breakout"),
|
||||
("Metro/Metro_RP2350_Chips_Challenge/", "Metro_RP2350_Chips_Challenge"),
|
||||
("Metro/Metro_RP2350_Minesweeper/", "Metro_RP2350_Minesweeper"),
|
||||
("Fruit_Jam/Larsio_Paint_Music/", "Larsio_Paint_Music"),
|
||||
("Fruit_Jam/Fruit_Jam_IRC_Client/", "Fruit_Jam_IRC_Client"),
|
||||
("Fruit_Jam/Fruit_Jam_PyPaint/", "Fruit_Jam_PyPaint"),
|
||||
]
|
||||
|
||||
def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir: Path, output_dir: Path):
|
||||
# Get font name without extension
|
||||
|
|
@ -19,6 +38,8 @@ def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir:
|
|||
try:
|
||||
# Copy src contents
|
||||
shutil.copytree(src_dir, temp_dir, dirs_exist_ok=True)
|
||||
# remove empty __init__.py file
|
||||
os.remove(temp_dir / "__init__.py")
|
||||
|
||||
# Create fonts directory and copy the specific font
|
||||
fonts_dir = temp_dir / "fonts"
|
||||
|
|
@ -28,66 +49,30 @@ def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir:
|
|||
# Extract learn-projects contents into apps directory
|
||||
apps_dir = temp_dir / "apps"
|
||||
apps_dir.mkdir(parents=True, exist_ok=True)
|
||||
for zip_path in learn_projects_dir.glob("*.zip"):
|
||||
# Create app-specific directory using zip name without extension
|
||||
app_name = zip_path.stem
|
||||
app_dir = apps_dir / app_name
|
||||
app_dir.mkdir(parents=True, exist_ok=True)
|
||||
# copy learn apps
|
||||
for learn_app_path, dir_name in LEARN_PROJECT_PATHS:
|
||||
shutil.copytree(f"Adafruit_Learning_System_Guides/{learn_app_path}", apps_dir / dir_name, dirs_exist_ok=True)
|
||||
|
||||
# Extract zip contents and process them
|
||||
with zipfile.ZipFile(zip_path, 'r') as zf:
|
||||
# Find the directory containing code.py
|
||||
code_dir = None
|
||||
for path in zf.namelist():
|
||||
if path.endswith('/code.py'):
|
||||
code_dir = str(Path(path).parent) + '/'
|
||||
break
|
||||
# copy builtin apps
|
||||
shutil.copytree("builtin_apps", apps_dir, dirs_exist_ok=True)
|
||||
shutil.copyfile("mock_boot_out.txt", temp_dir / "boot_out.txt")
|
||||
|
||||
if not code_dir:
|
||||
print(f"Warning: No code.py found in {zip_path}")
|
||||
continue
|
||||
|
||||
# Extract files from the code.py directory to app directory
|
||||
for path in zf.namelist():
|
||||
if path.startswith(code_dir):
|
||||
# Skip the lib directory as we'll handle it separately
|
||||
if 'lib/' in path:
|
||||
continue
|
||||
|
||||
# Get the relative path from code_dir
|
||||
rel_path = path[len(code_dir):]
|
||||
if rel_path:
|
||||
# Extract the file
|
||||
source = zf.open(path)
|
||||
target = app_dir / rel_path
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(target, 'wb') as f:
|
||||
f.write(source.read())
|
||||
|
||||
# Handle lib directory specially - move to root
|
||||
for path in zf.namelist():
|
||||
if '/lib/' in path:
|
||||
# Get the part of the path after 'lib/'
|
||||
lib_index = path.index('/lib/') + 5 # skip past '/lib/'
|
||||
rel_path = path[lib_index:]
|
||||
|
||||
# Skip directory entries
|
||||
if not rel_path or path.endswith('/'):
|
||||
continue
|
||||
|
||||
# Extract the file to root lib directory
|
||||
source = zf.open(path)
|
||||
target = temp_dir / 'lib' / rel_path
|
||||
# Ensure parent directory exists
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
# Write the file
|
||||
with open(target, 'wb') as f:
|
||||
f.write(source.read())
|
||||
# install launcher required libs
|
||||
circup_cli(["--path", temp_dir, "install", "--auto"],
|
||||
standalone_mode=False)
|
||||
|
||||
# install apps required libs
|
||||
for app_dir in os.listdir(apps_dir):
|
||||
circup_cli(["--path", temp_dir, "install", "--auto", "--auto-file", f"apps/{app_dir}/code.py"],
|
||||
standalone_mode=False)
|
||||
os.remove(temp_dir / "boot_out.txt")
|
||||
# Create the final zip file
|
||||
with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||
for file_path in temp_dir.rglob("*"):
|
||||
if file_path.is_file():
|
||||
modification_time = datetime(2000, 1, 1, 0, 0, 0)
|
||||
modification_timestamp = modification_time.timestamp()
|
||||
os.utime(file_path, (modification_timestamp, modification_timestamp))
|
||||
arcname = file_path.relative_to(temp_dir)
|
||||
zf.write(file_path, arcname)
|
||||
|
||||
|
|
@ -97,7 +82,21 @@ def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir:
|
|||
# Clean up temporary directory
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
|
||||
|
||||
def download_learn_projects():
|
||||
try:
|
||||
shutil.rmtree("Adafruit_Learning_System_Guides/")
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
os.system("git clone https://github.com/adafruit/Adafruit_Learning_System_Guides.git")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# download all learn project zips
|
||||
download_learn_projects()
|
||||
|
||||
# Get the project root directory
|
||||
root_dir = Path(__file__).parent
|
||||
|
||||
|
|
@ -107,6 +106,10 @@ def main():
|
|||
learn_projects_dir = root_dir / "learn-projects"
|
||||
output_dir = root_dir / "dist"
|
||||
|
||||
# delete output dir if it exists
|
||||
if output_dir.exists():
|
||||
shutil.rmtree(output_dir)
|
||||
|
||||
# Create output directory
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
|
|
|||
674
builtin_apps/PyBasic/LICENSE
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
22
builtin_apps/PyBasic/PyBasic changes.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
Changes made to https://github.com/richpl/PyBasic on 8/6/2025 to support CircuitPython and Fruit-Jam-OS.
|
||||
|
||||
1) interpreter.py (modified printing of trapped exceptions for CircuitPython):
|
||||
|
||||
Added the following import (line 30):
|
||||
from traceback import print_exception
|
||||
|
||||
Replaced exception print (line 130):
|
||||
Orig: print(e, file=stderr, flush=True)
|
||||
New: print_exception(e,e,e.__traceback__ if hasattr(e,'__traceback__') else None)
|
||||
|
||||
2) program.py (set the default directory to the same directory a program is loaded from):
|
||||
|
||||
Added the following import (line 28):
|
||||
import os
|
||||
|
||||
Added command to set current directory to folder loaded program is loaded from (line 236-239):
|
||||
if file.rfind('/') == 0:
|
||||
os.chdir('/')
|
||||
elif file.rfind('/') != -1:
|
||||
os.chdir(file[:file.rfind('/')])
|
||||
|
||||
BIN
builtin_apps/PyBasic/PyBasic.bmp
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
7
builtin_apps/PyBasic/PyBasic.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Launches the PyBasic interpreter
|
||||
#
|
||||
# See the "Pybasic changes.txt" file for a list of difference from
|
||||
# the original https://github.com/richpl/PyBasic
|
||||
#
|
||||
import interpreter
|
||||
interpreter.main()
|
||||
1089
builtin_apps/PyBasic/README.md
Normal file
1798
builtin_apps/PyBasic/basicparser.py
Normal file
200
builtin_apps/PyBasic/basictoken.py
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""Class to represent a token for the BASIC
|
||||
programming language. A token consists of
|
||||
three items:
|
||||
|
||||
column Column in which token starts
|
||||
category Category of the token
|
||||
lexeme Token in string form
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class BASICToken:
|
||||
|
||||
"""BASICToken categories"""
|
||||
|
||||
EOF = 0 # End of file
|
||||
LET = 1 # LET keyword
|
||||
LIST = 2 # LIST command
|
||||
PRINT = 3 # PRINT command
|
||||
RUN = 4 # RUN command
|
||||
FOR = 5 # FOR keyword
|
||||
NEXT = 6 # NEXT keyword
|
||||
IF = 7 # IF keyword
|
||||
THEN = 8 # THEN keyword
|
||||
ELSE = 9 # ELSE keyword
|
||||
ASSIGNOP = 10 # '='
|
||||
LEFTPAREN = 11 # '('
|
||||
RIGHTPAREN = 12 # ')'
|
||||
PLUS = 13 # '+'
|
||||
MINUS = 14 # '-'
|
||||
TIMES = 15 # '*'
|
||||
DIVIDE = 16 # '/'
|
||||
NEWLINE = 17 # End of line
|
||||
UNSIGNEDINT = 18 # Integer
|
||||
NAME = 19 # Identifier that is not a keyword
|
||||
EXIT = 20 # Used to quit the interpreter
|
||||
DIM = 21 # DIM keyword
|
||||
GREATER = 22 # '>'
|
||||
LESSER = 23 # '<'
|
||||
STEP = 24 # STEP keyword
|
||||
GOTO = 25 # GOTO keyword
|
||||
GOSUB = 26 # GOSUB keyword
|
||||
INPUT = 27 # INPUT keyword
|
||||
REM = 28 # REM keyword
|
||||
RETURN = 29 # RETURN keyword
|
||||
SAVE = 30 # SAVE command
|
||||
LOAD = 31 # LOAD command
|
||||
NOTEQUAL = 32 # '<>'
|
||||
LESSEQUAL = 33 # '<='
|
||||
GREATEQUAL = 34 # '>='
|
||||
UNSIGNEDFLOAT = 35 # Floating point number
|
||||
STRING = 36 # String values
|
||||
TO = 37 # TO keyword
|
||||
NEW = 38 # NEW command
|
||||
EQUAL = 39 # '='
|
||||
COMMA = 40 # ','
|
||||
STOP = 41 # STOP keyword
|
||||
COLON = 42 # ':'
|
||||
ON = 43 # ON keyword
|
||||
POW = 44 # Power function
|
||||
SQR = 45 # Square root function
|
||||
ABS = 46 # Absolute value function
|
||||
DIM = 47 # DIM keyword
|
||||
RANDOMIZE = 48 # RANDOMIZE keyword
|
||||
RND = 49 # RND keyword
|
||||
ATN = 50 # Arctangent function
|
||||
COS = 51 # Cosine function
|
||||
EXP = 52 # Exponential function
|
||||
LOG = 53 # Natural logarithm function
|
||||
SIN = 54 # Sine function
|
||||
TAN = 55 # Tangent function
|
||||
DATA = 56 # DATA keyword
|
||||
READ = 57 # READ keyword
|
||||
INT = 58 # INT function
|
||||
CHR = 59 # CHR$ function
|
||||
ASC = 60 # ASC function
|
||||
STR = 61 # STR$ function
|
||||
MID = 62 # MID$ function
|
||||
MODULO = 63 # MODULO operator
|
||||
TERNARY = 64 # TERNARY functions
|
||||
VAL = 65 # VAL function
|
||||
LEN = 66 # LEN function
|
||||
UPPER = 67 # UPPER function
|
||||
LOWER = 68 # LOWER function
|
||||
ROUND = 69 # ROUND function
|
||||
MAX = 70 # MAX function
|
||||
MIN = 71 # MIN function
|
||||
INSTR = 72 # INSTR function
|
||||
AND = 73 # AND operator
|
||||
OR = 74 # OR operator
|
||||
NOT = 75 # NOT operator
|
||||
PI = 76 # PI constant
|
||||
RNDINT = 77 # RNDINT function
|
||||
OPEN = 78 # OPEN keyword
|
||||
HASH = 79 # "#"
|
||||
CLOSE = 80 # CLOSE keyword
|
||||
FSEEK = 81 # FSEEK keyword
|
||||
RESTORE = 82 # RESTORE keyword
|
||||
APPEND = 83 # APPEND keyword
|
||||
OUTPUT = 84 # OUTPUT keyword
|
||||
TAB = 85 # TAB function
|
||||
SEMICOLON = 86 # SEMICOLON
|
||||
LEFT = 87 # LEFT$ function
|
||||
RIGHT = 88 # RIGHT$ function
|
||||
|
||||
# Displayable names for each token category
|
||||
catnames = ['EOF', 'LET', 'LIST', 'PRINT', 'RUN',
|
||||
'FOR', 'NEXT', 'IF', 'THEN', 'ELSE', 'ASSIGNOP',
|
||||
'LEFTPAREN', 'RIGHTPAREN', 'PLUS', 'MINUS', 'TIMES',
|
||||
'DIVIDE', 'NEWLINE', 'UNSIGNEDINT', 'NAME', 'EXIT',
|
||||
'DIM', 'GREATER', 'LESSER', 'STEP', 'GOTO', 'GOSUB',
|
||||
'INPUT', 'REM', 'RETURN', 'SAVE', 'LOAD',
|
||||
'NOTEQUAL', 'LESSEQUAL', 'GREATEQUAL',
|
||||
'UNSIGNEDFLOAT', 'STRING', 'TO', 'NEW', 'EQUAL',
|
||||
'COMMA', 'STOP', 'COLON', 'ON', 'POW', 'SQR', 'ABS',
|
||||
'DIM', 'RANDOMIZE', 'RND', 'ATN', 'COS', 'EXP',
|
||||
'LOG', 'SIN', 'TAN', 'DATA', 'READ', 'INT',
|
||||
'CHR', 'ASC', 'STR', 'MID', 'MODULO', 'TERNARY',
|
||||
'VAL', 'LEN', 'UPPER', 'LOWER', 'ROUND',
|
||||
'MAX', 'MIN', 'INSTR', 'AND', 'OR', 'NOT', 'PI',
|
||||
'RNDINT', 'OPEN', 'HASH', 'CLOSE', 'FSEEK', 'APPEND',
|
||||
'OUTPUT', 'RESTORE', 'RNDINT', 'TAB', 'SEMICOLON',
|
||||
'LEFT', 'RIGHT']
|
||||
|
||||
smalltokens = {'=': ASSIGNOP, '(': LEFTPAREN, ')': RIGHTPAREN,
|
||||
'+': PLUS, '-': MINUS, '*': TIMES, '/': DIVIDE,
|
||||
'\n': NEWLINE, '<': LESSER,
|
||||
'>': GREATER, '<>': NOTEQUAL,
|
||||
'<=': LESSEQUAL, '>=': GREATEQUAL, ',': COMMA,
|
||||
':': COLON, '%': MODULO, '!=': NOTEQUAL, '#': HASH,
|
||||
';': SEMICOLON}
|
||||
|
||||
|
||||
# Dictionary of BASIC reserved words
|
||||
keywords = {'LET': LET, 'LIST': LIST, 'PRINT': PRINT,
|
||||
'FOR': FOR, 'RUN': RUN, 'NEXT': NEXT,
|
||||
'IF': IF, 'THEN': THEN, 'ELSE': ELSE,
|
||||
'EXIT': EXIT, 'DIM': DIM, 'STEP': STEP,
|
||||
'GOTO': GOTO, 'GOSUB': GOSUB,
|
||||
'INPUT': INPUT, 'REM': REM, 'RETURN': RETURN,
|
||||
'SAVE': SAVE, 'LOAD': LOAD, 'NEW': NEW,
|
||||
'STOP': STOP, 'TO': TO, 'ON':ON, 'POW': POW,
|
||||
'SQR': SQR, 'ABS': ABS,
|
||||
'RANDOMIZE': RANDOMIZE, 'RND': RND,
|
||||
'ATN': ATN, 'COS': COS, 'EXP': EXP,
|
||||
'LOG': LOG, 'SIN': SIN, 'TAN': TAN,
|
||||
'DATA': DATA, 'READ': READ, 'INT': INT,
|
||||
'CHR$': CHR, 'ASC': ASC, 'STR$': STR,
|
||||
'MID$': MID, 'MOD': MODULO,
|
||||
'IF$': TERNARY, 'IFF': TERNARY,
|
||||
'VAL': VAL, 'LEN': LEN,
|
||||
'UPPER$': UPPER, 'LOWER$': LOWER,
|
||||
'ROUND': ROUND, 'MAX': MAX, 'MIN': MIN,
|
||||
'INSTR': INSTR, 'END': STOP,
|
||||
'AND': AND, 'OR': OR, 'NOT': NOT,
|
||||
'PI': PI, 'RNDINT': RNDINT, 'OPEN': OPEN,
|
||||
'CLOSE': CLOSE, 'FSEEK': FSEEK,
|
||||
'APPEND': APPEND, 'OUTPUT':OUTPUT,
|
||||
'RESTORE': RESTORE, 'TAB': TAB,
|
||||
'LEFT$': LEFT, 'RIGHT$': RIGHT}
|
||||
|
||||
|
||||
# Functions
|
||||
functions = {ABS, ATN, COS, EXP, INT, LOG, POW, RND, SIN, SQR, TAN,
|
||||
CHR, ASC, MID, TERNARY, STR, VAL, LEN, UPPER, LOWER,
|
||||
ROUND, MAX, MIN, INSTR, PI, RNDINT, TAB, LEFT, RIGHT}
|
||||
|
||||
def __init__(self, column, category, lexeme):
|
||||
|
||||
self.column = column # Column in which token starts
|
||||
self.category = category # Category of the token
|
||||
self.lexeme = lexeme # Token in string form
|
||||
|
||||
def pretty_print(self):
|
||||
"""Pretty prints the token
|
||||
|
||||
"""
|
||||
print('Column:', self.column,
|
||||
'Category:', self.catnames[self.category],
|
||||
'Lexeme:', self.lexeme)
|
||||
|
||||
def print_lexeme(self):
|
||||
print(self.lexeme, end=' ')
|
||||
4
builtin_apps/PyBasic/code.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
from supervisor import reload
|
||||
import interpreter
|
||||
interpreter.main()
|
||||
reload()
|
||||
100
builtin_apps/PyBasic/examples/ADESCRIP
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
You're in the forest.
|
||||
You're in the forest.
|
||||
You're in the forest.
|
||||
You're in the forest.
|
||||
You're at the hill in the road.
|
||||
You're at the end of the road again.
|
||||
You're inside the Building.
|
||||
You're in the valley.
|
||||
You're at slit in the streambed.
|
||||
You're directly above a grate in a dry streambed.
|
||||
You're below the grate.
|
||||
You're in the cobble crawl.
|
||||
You're in the debris room.
|
||||
You are in an awkward sloping east/west canyon.
|
||||
You're in the bird chamber.
|
||||
You're at the top of the small pit.
|
||||
You're in the Hall of Mists.
|
||||
You're in the nugget of gold room.
|
||||
You're on the east bank of the fissure.
|
||||
You are on the west side of the fissure in the Hall of Mists.
|
||||
You're at west end of the Hall of Mists.
|
||||
You're in the Hall of the Mountain King.
|
||||
You are in the south side chamber.
|
||||
You are in the west side chamber of the Hall of the Mountain King.
|
||||
You are at a N/S passage at a hole in the floor.
|
||||
You're at Y2 .
|
||||
You're at the window near the pit.
|
||||
The passage here is blocked by a recent cave-in.
|
||||
You're at the east end of the Long Hall.
|
||||
You're at west end of the Long Hall.
|
||||
You are at a crossover of a high N/S passage and a low E/W one.
|
||||
Dead end
|
||||
You are in a dirty broken passage.
|
||||
You're at the top of the small pit.
|
||||
You are in a little pit which has a stream flowing through it.
|
||||
You're in the dusty rock room.
|
||||
You're at a complex junction.
|
||||
You're in the anteroom.
|
||||
You are at Witt's end. Passages lead off in *all* directions.
|
||||
You're in the shell room.
|
||||
You're in the arched hall.
|
||||
You're in a long sloping corridor with ragged sharp walls.
|
||||
You are in a cul-de-sac about eight feet across. How's your french?
|
||||
You are in bedquilt, a long east/west passage with holes everywhere.
|
||||
You're in the swiss cheese room.
|
||||
You're in the soft room.
|
||||
You're at the east end of the Twopit room.
|
||||
You're at the west end of the Twopit room.
|
||||
You're in the east pit.
|
||||
You're in the west pit.
|
||||
You're in the slab room.
|
||||
You're in the Oriental room.
|
||||
You are in a large low room.
|
||||
You're in a sloping corridor.
|
||||
Dead end crawl.
|
||||
You're in the misty cavern.
|
||||
You're in the alcove.
|
||||
You're in the Plover room.
|
||||
You're in the dark-room.
|
||||
You're on the SW side of the chasm.
|
||||
You're on the NE side of the chasm.
|
||||
You're in a corridor.
|
||||
You're at the fork in the path.
|
||||
You're in the limestone passage.
|
||||
You're at a junction with warm walls.
|
||||
You're in the chamber of boulders.
|
||||
You're at a breath-taking view.
|
||||
You're in front of the barren room.
|
||||
You're in the barren room.
|
||||
You're in a narrow corridor.
|
||||
You're in the Giant room.
|
||||
The passage here is blocked by a recent cave-in.
|
||||
You are at one end of an immense north/south passage.
|
||||
You're in the cavern with a waterfall
|
||||
You're at a steep incline above a large room.
|
||||
You are in a secret N/S canyon above a sizable passage.
|
||||
You're at the top of the stalactite.
|
||||
You're at a junction of three secret canyons.
|
||||
You are in a secret N/S canyon above a large room.
|
||||
You're in mirror canyon.
|
||||
You're at a reservoir.
|
||||
You are in a secret canyon which exits to the north and east.
|
||||
You're in the secret E/W canyon above a tight canyon.
|
||||
You are at a wide place in a very tight N/S canyon.
|
||||
The canyon here becomes too tight to go on.
|
||||
You are in a tall E/W canyon.
|
||||
The canyon runs into a mass of boulders -- dead end.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are at a huge orange stalactite in the maze.
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
You are at a dead end.
|
||||
35
builtin_apps/PyBasic/examples/AITEMS
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
There is a large sparking nugget of gold here!
|
||||
There are bars of silver here!
|
||||
There is precious jewelry here!
|
||||
There are many coins here!
|
||||
There are several diamonds here!
|
||||
There is a delicate, precious ming vase here!
|
||||
To one side lies a glistening Pearl!
|
||||
There is a nest here, full of golden eggs!
|
||||
There is a jewel-encrusted trident here!
|
||||
There is an emerald the size of a plover's egg here!
|
||||
There is a platinum pyramid here, eight inches on a side!
|
||||
There is a golden chain here!
|
||||
There are rare spices here!
|
||||
There is a valuable persian rug here!
|
||||
There is a chest here, full of various treasures!
|
||||
There is a bottle of water here.
|
||||
There is a small pool of oil here.
|
||||
There is a brass lantern here.
|
||||
There is a set of keys here.
|
||||
There is food here.
|
||||
There is a glass bottle here.
|
||||
There is a small wicker cage here.
|
||||
There is a 3-foot black rod here.
|
||||
There is an enormous clam here with its shell tightly shut.
|
||||
There is a recent issue of 'Spelunker Today' here.
|
||||
There is a bear nearby.
|
||||
There is a little axe here.
|
||||
There is a purple velvet pillow here.
|
||||
There are shards of pottery scattered about.
|
||||
There is an enormous oyster here with its shell tightly shut.
|
||||
There is a little bird here.
|
||||
A burly troll stands in front of you.
|
||||
A huge fierce green dragon bars the way!
|
||||
A huge fierce green snake bars the way!
|
||||
There is a threatening little dwarf in the room with you!
|
||||
583
builtin_apps/PyBasic/examples/AMESSAGE
Normal file
|
|
@ -0,0 +1,583 @@
|
|||
#1
|
||||
You can't move that way.
|
||||
#2.1
|
||||
Nothing happens.
|
||||
#2.2
|
||||
Nothing seems to happen.
|
||||
#2.3
|
||||
I don't think that had any affect on anything.
|
||||
#2.4
|
||||
I don't know how to apply that word here.
|
||||
#2.5
|
||||
That had little effect, if any at all.
|
||||
#3
|
||||
The mist is quite thick here, and the fissure is too wide to jump.
|
||||
#4
|
||||
You are at the bottom of the pit with a broken neck.
|
||||
#5
|
||||
The bottle is already filled!
|
||||
#6
|
||||
The bear eagerly wolfs down your food, after which he seems to calm
|
||||
down considerably and even becomes rather friendly. burp
|
||||
#7
|
||||
The plant surges upwards, reaching halfway to the hole above you
|
||||
#8
|
||||
The plant grows explosively, almost filling the bottom of the pit.
|
||||
#9
|
||||
You overwatered the plant, you fool!
|
||||
#10
|
||||
The grate is locked.
|
||||
#11
|
||||
The grate is unlocked.
|
||||
#12
|
||||
The bear is quite ferocious, and will no doubt tear you to
|
||||
shreds if you approach it!
|
||||
#13
|
||||
The chain is unlocked and the bear is free.
|
||||
#14
|
||||
A crystal bridge now spans the fissure.
|
||||
#15
|
||||
The bridge vanishes.
|
||||
#16
|
||||
It is beyond your power to do that.
|
||||
#17
|
||||
The clam opens momenntarily, and a glistening pearl falls out
|
||||
and rolls away. Goodness, this must really have been an oyster.
|
||||
(I never was any good at identifying bivalves, anyway.)
|
||||
#18
|
||||
I can't close that!
|
||||
#19
|
||||
The oil has freed up the hinges so that the door will now move,
|
||||
although it requires some effort.
|
||||
#20
|
||||
I can't eat.
|
||||
#21
|
||||
How can I drink that?
|
||||
#22
|
||||
"Glug, glug, glug, Belch!"
|
||||
#23
|
||||
It doesn't want to eat anything (except maybe you!)
|
||||
#24
|
||||
You fool! Dwarves only eat coal! Now you've made him
|
||||
*** REALLY MAD ****!
|
||||
#25
|
||||
Trolls are close relatives with the rocks and have skin as tough as
|
||||
that of a rhinoceros. The troll fends off your blows effortlessly.
|
||||
#26
|
||||
The troll deftly catches the axe, examines it carefully, and tosses it
|
||||
back, declaring, "Good workmanship, but it's not valuable enough."
|
||||
#27
|
||||
The troll catches your treasure and scurries away out of sight.
|
||||
#28
|
||||
The bear lumbers toward the troll, who lets out a startled shriek and
|
||||
scurries away. The bear soon gives up the pursuit and wanders back.
|
||||
#29
|
||||
You attack a little dwarf, but he dodges out of the way.
|
||||
#30
|
||||
You killed a little dwarf. The body vanishes in a cloud of greasy
|
||||
black smoke.
|
||||
#31
|
||||
There is a threatening little dwarf in the room with you!
|
||||
#32
|
||||
One sharp nasty knife is thrown at you!
|
||||
#33
|
||||
Out from the shadows behind you pounces a bearded pirate! "Har, har,"
|
||||
he chortles, "I'll just take all this booty and hide it away with me
|
||||
chest deep in the maze!" he snatches your treasure and vanishes into
|
||||
the gloom.
|
||||
#34
|
||||
There are faint rustling noises from the darkness behind you.
|
||||
#35
|
||||
The grate is open.
|
||||
#36
|
||||
The grate is locked.
|
||||
#37
|
||||
The bird was unafraid when you entered, but as you approach it becomes
|
||||
disturbed and you cannot catch it.
|
||||
#38
|
||||
The dome is unclimbable.
|
||||
#39
|
||||
A crystal bridge now spans the fissure.
|
||||
#40
|
||||
A huge green fierce snake bars the way! Hisssssss
|
||||
#41
|
||||
A hollow voice says "Plugh".
|
||||
#42
|
||||
The vase is now resting, delicately, on a velvet pillow.
|
||||
#43
|
||||
Crash! Tinkle! The ming vase shatters into worthless crockery.
|
||||
#44
|
||||
You fell into a pit and broke every bone in your body!
|
||||
#45
|
||||
It is now pitch dark. If you proceed you will likely fall into a pit.
|
||||
#46
|
||||
The nest of golden eggs has vanished!
|
||||
#47
|
||||
There is a tiny little plant in the pit, murmuring "water, water, ..."
|
||||
#48
|
||||
There is a 12-foot-tall beanstalk stretching up out of the pit,
|
||||
bellowing "WATER!! WATER!!"
|
||||
#49
|
||||
There is a gigantic beanstalk stretching all the way up to the hole.
|
||||
#50
|
||||
You can't get by the snake. He hates your guts.
|
||||
#51
|
||||
The dragon has a rather "volatile" personality, and he will
|
||||
probably incinerate you if you get any closer.
|
||||
#52
|
||||
You have crawled around in some little holes and wound up back in the
|
||||
main passage.
|
||||
#53
|
||||
Something you're carrying won't fit through the tunnel with you.
|
||||
#54
|
||||
Your load is too heavy. You'd best take inventory and drop
|
||||
something first.
|
||||
#55
|
||||
A burly troll stands by the bridge and insists you throw him a
|
||||
treasure before you may cross.
|
||||
#56
|
||||
The troll pops from under the bridge and blocks your way.
|
||||
#57
|
||||
The way north is barred by a massive, rusty, iron door.
|
||||
#58
|
||||
The bear will bite off your hand. Besides, the chain is locked to the wall.
|
||||
#59
|
||||
It's rather difficult to move a twenty-ton dragon, considering
|
||||
he is lying on the rug.
|
||||
#60
|
||||
The vase is now resting, delicately, on a velvet pillow.
|
||||
#61.1
|
||||
A valliant attempt!
|
||||
#61.2
|
||||
Nice try!
|
||||
#61.3
|
||||
A valliant attempt, but you're not that strong!
|
||||
#61.4
|
||||
Don't be silly!
|
||||
#61.5
|
||||
Do you have any idea on how to do that?
|
||||
#62
|
||||
A huge green fierce dragon blocks the way. The dragon is lying
|
||||
on a persian rug!
|
||||
#63
|
||||
A burly troll stands beside the bridge, blocking your way.
|
||||
#64
|
||||
There is a ferocious cave bear eyeing you from the far end of the room!
|
||||
#65
|
||||
The bear is locked to the wall with a golden chain!
|
||||
#66
|
||||
There is a calm, friendly bear locked to the wall.
|
||||
#67
|
||||
You are being followed by a very large, tame bear.
|
||||
#68
|
||||
With what? Your bare hands?
|
||||
#69
|
||||
Congratulations! You have just vanquished a dragon with your bare hands
|
||||
(hard to believe ain't it?).
|
||||
#70
|
||||
Attacking it is both dangerous and doesn't work.
|
||||
#71
|
||||
There is nothing here to attack.
|
||||
#72
|
||||
How can you do that to more than one thing at a time?
|
||||
#73
|
||||
Thank you, it was delicious!
|
||||
#74
|
||||
I see nothing special about that.
|
||||
#75
|
||||
Oh dear, you seem to have gotten yourself killed. I might be able to
|
||||
help you out, but I've never really done this before. Do you want me
|
||||
to try to reincarnate you?
|
||||
#76
|
||||
All right. But don't blame me if something goes wr......
|
||||
--- POOF!! ---
|
||||
You are engulfed in a cloud of orange smoke. Coughing and gasping,
|
||||
you emerge from the smoke and find....
|
||||
#77
|
||||
You clumsy oaf, you've done it again! I don't know how long I can
|
||||
keep this up. Do you want me to try reincarnating you again?
|
||||
#78
|
||||
Now you've really done it! I'm out of orange smoke! You don't expect
|
||||
me to do a decent reincarnation without any orange smoke, do you?
|
||||
Better luck next time!
|
||||
#79
|
||||
The nest of golden eggs has vanished!
|
||||
#80
|
||||
A little dwarf just walked around a corner, saw you, threw a little
|
||||
axe at you which missed, cursed and ran away.
|
||||
#81
|
||||
The nest of golden eggs has re-appeared!
|
||||
#200
|
||||
You are in a forest, with trees all around you.
|
||||
#205
|
||||
You have walked up a hill, still in the forest. The road slopes back
|
||||
down the other side of the hill. There is a building in the distance.
|
||||
#206
|
||||
You are standing at the end of a road before a small brick building.
|
||||
Around you is a forest. A small stream flows out of the building and
|
||||
down a gully.
|
||||
#207
|
||||
You are inside a building, a well house for a large spring.
|
||||
#208
|
||||
You are in a valley in the forest beside a stream tumbling along a
|
||||
rocky bed.
|
||||
#209
|
||||
At your feet all the water of the stream splashes into a 2-inch slit
|
||||
in the rock. Downstream the streambed is bare rock.
|
||||
#210
|
||||
You are in a 20-foot depression floored with bare dirt. Set into the
|
||||
dirt is a strong steel grate mounted in concrete. A dry streambed
|
||||
leads into the depression.
|
||||
#211
|
||||
You are in a small chamber beneath a 3x3 steel grate to the surface.
|
||||
A low crawl over cobbles leads inward to the west.
|
||||
#212
|
||||
You are crawling over cobbles in a low passage. There is a dim light
|
||||
at the east end of the passage.
|
||||
#213
|
||||
You are in a debris room filled with stuff washed in from the surface.
|
||||
A low wide passage with cobbles becomes plugged with mud and debris
|
||||
here, but an awkward canyon leads upward and west. A note on the wall
|
||||
says "magic word XYZZY."
|
||||
#214
|
||||
You are in an awkward sloping east/west canyon.
|
||||
#215
|
||||
You are in a splendid chamber thirty feet high. The walls are frozen
|
||||
rivers of orange stone. An awkward canyon and a good passage exit
|
||||
from east and west sides of the chamber.
|
||||
#216
|
||||
At your feet is a small pit breathing traces of white mist. An east
|
||||
passage ends here except for a small crack leading on.
|
||||
|
||||
Rough stone steps lead down the pit.
|
||||
#217
|
||||
You are at one end of a vast hall stretching forward out of sight to
|
||||
the west. There are openings to either side. Nearby, a wide stone
|
||||
staircase leads downward. The hall is filled with wisps of white mist
|
||||
swaying to and fro almost as if alive. A cold wind blows up the
|
||||
staircase. There is a passage at the top of a dome behind you.
|
||||
|
||||
Rough stone steps lead to the top of the dome.
|
||||
#218
|
||||
This is a low room with a crude note on the wall. The note says,
|
||||
"you can't get it up the steps."
|
||||
#219
|
||||
You are on the east bank of a fissure slicing clear across the hall.
|
||||
The mist is quite thick here, and the fissure is too wide to jump.
|
||||
#220
|
||||
You are on the west side of the fissure in the Hall of Mists.
|
||||
#221
|
||||
You are at the west end of Hall of Mists. A low wide crawl continues
|
||||
west and another goes north. To the south is a little passage 6 feet
|
||||
off the floor.
|
||||
#222
|
||||
You are in the Hall of the Mountain King, with passages off in all
|
||||
directions.
|
||||
#223
|
||||
You are in the south side chamber.
|
||||
#224
|
||||
You are in the west side chamber of the Hall of the Mountain King.
|
||||
A passage continues west and up here.
|
||||
#225
|
||||
You are in a low N/S passage at a hole in the floor. The hole goes
|
||||
down to an E/W passage.
|
||||
#226
|
||||
You are in a large room, with a passage to the south, a passage to the
|
||||
west, and a wall of broken rock to the east. There is a large "Y2" on
|
||||
a rock in the room's center.
|
||||
#227
|
||||
You're at a low window overlooking a huge pit, which extends up out of
|
||||
sight. A floor is indistinctly visible over 50 feet below. Traces of
|
||||
white mist cover the floor of the pit, becoming thicker to the right.
|
||||
Marks in the dust around the window would seem to indicate that
|
||||
someone has been here recently. Directly across the pit from you and
|
||||
25 feet away there is a similar window looking into a lighted room. A
|
||||
shadowy figure can be seen there peering back at you.
|
||||
|
||||
The shadowy figure seems to be trying to attract your attention.
|
||||
#228
|
||||
The passage here is blocked by a recent cave-in.
|
||||
#229
|
||||
You are at the east end of a very long hall apparently without side
|
||||
chambers. To the east a low wide crawl slants up. To the north a
|
||||
round two foot hole slants down.
|
||||
#230
|
||||
You are at the west end of a very long featureless hall. The hall
|
||||
joins up with a narrow north/south passage.
|
||||
#231
|
||||
You are at a crossover of a high N/S passage and a low E/W one.
|
||||
#232
|
||||
Dead end
|
||||
#233
|
||||
You are in a dirty broken passage. To the east is a crawl. To the
|
||||
west is a large passage. Above you is a hole to another passage.
|
||||
#234
|
||||
You are on the brink of a small clean climbable pit. A crawl leads
|
||||
west.
|
||||
#235
|
||||
You are in the bottom of a small pit with a little stream, which
|
||||
enters and exits through tiny slits.
|
||||
#236
|
||||
You are in a large room full of dusty rocks. There is a big hole in
|
||||
the floor. There are cracks everywhere, and a passage leading east.
|
||||
#237
|
||||
You are at a complex junction. A low hands and knees passage from the
|
||||
north joins a higher crawl from the east to make a walking passage
|
||||
going west. There is also a large room above. The air is damp here.
|
||||
#238
|
||||
You are in an anteroom leading to a large passage to the east. Small
|
||||
passages go west and up. The remnants of recent digging are evident.
|
||||
a sign in midair here says "Cave under construction beyond this point.
|
||||
proceed at own risk. "[Witt Construction Company]"
|
||||
#239
|
||||
You are at Witt's end. Passages lead off in *all* directions.
|
||||
#240
|
||||
You're in a large room carved out of sedimentary rock. The floor and
|
||||
walls are littered with bits of shells embedded in the stone. A
|
||||
shallow passage proceeds downward, and a somewhat steeper one leads
|
||||
up. A low hands and knees passage enters from the south.
|
||||
#241
|
||||
You are in an arched hall. A coral passage once continued up and east
|
||||
from here, but is now blocked by debris. The air smells of sea water.
|
||||
#242
|
||||
You are in a long sloping corridor with ragged sharp walls.
|
||||
#243
|
||||
You are in a cul-de-sac about eight feet across. How's your french?
|
||||
#244
|
||||
You are in bedquilt, a long east/west passage with holes everywhere.
|
||||
To explore at random, select north, south, up or down.
|
||||
#245
|
||||
You are in a room whose walls resemble swiss cheese. Obvious passages
|
||||
go west, east, NE, and NW. Part of the room is occupied by a large
|
||||
bedrock block.
|
||||
#246
|
||||
You are in the soft room. The walls are covered with heavy curtains,
|
||||
the floor with a thick pile carpet. Moss covers the ceiling.
|
||||
#247
|
||||
You are at the east end of the Twopit room. The floor here is
|
||||
littered with thin rock slabs, which make it easy to descend the pits.
|
||||
There is a path here bypassing the pits to connect passages from east
|
||||
and west. There are holes all over, but the only big one is on the
|
||||
wall directly over the west pit where you can't get to it.
|
||||
#248
|
||||
You are at the west end of the Twopit room. There is a large hole in
|
||||
the wall above the pit at this end of the room.
|
||||
#249
|
||||
You are at the bottom of the eastern pit in the Twopit room. There is
|
||||
a small pool of oil in one corner of the pit.
|
||||
#250
|
||||
You are at the bottom of the western pit in the Twopit room. There is
|
||||
a large hole in the wall about 25 feet above you.
|
||||
#251
|
||||
You are in a large low circular chamber whose floor is an immense slab
|
||||
fallen from the ceiling (slab room). East and west there once were
|
||||
large passages, but they are now filled with boulders. Low small
|
||||
passages go north and south, and the south one quickly bends west
|
||||
around the boulders.
|
||||
#252
|
||||
This is the oriental room. Ancient oriental cave drawings cover the
|
||||
walls. A gently sloping passage leads upward to the north, another
|
||||
passage leads SE, and a hands and knees crawl leads west.
|
||||
#253
|
||||
You are in a large low room. Crawls lead north, SE, and SW.
|
||||
#254
|
||||
You are in a long winding corridor sloping out of sight in both
|
||||
directions.
|
||||
#255
|
||||
Dead end crawl.
|
||||
#256
|
||||
You are following a wide path around the outer edge of a large cavern.
|
||||
Far below, through a heavy white mist, strange splashing noises can be
|
||||
heard. The mist rises up through a fissure in the ceiling. The path
|
||||
exits to the south and west.
|
||||
#257
|
||||
You are in an alcove. A small NW path seems to widen after a short
|
||||
distance. An extremely tight tunnel leads east. It looks like a very
|
||||
tight squeeze. An eerie light can be seen at the other end.
|
||||
#258
|
||||
You're in a small chamber lit by an eerie green light. An extremely
|
||||
narrow tunnel exits to the west. A dark corridor leads NE.
|
||||
#259
|
||||
You're in the dark-room. A corridor leading south is the only exit.
|
||||
A massive stone tablet embedded in the wall reads:
|
||||
"Congratulations on bringing light into the dark-room!"
|
||||
#260
|
||||
You are on one side of a large, deep chasm. A heavy white mist rising
|
||||
up from below obscures all view of the far side. A SW path leads away.
|
||||
|
||||
A rickety wooden bridge extends across the chasm, vanishing into the
|
||||
mist. A sign posted on the bridge reads, "STOP! Pay Troll!"
|
||||
#261
|
||||
You are on the far side of the chasm. A NE path leads away from the
|
||||
chasm on this side.
|
||||
|
||||
A rickety wooden bridge extends across the chasm, vanishing into the
|
||||
gloom. A sign posted on the bridge reads: "Stop! Pay troll!"
|
||||
#262
|
||||
You're in a long east/west corridor. A faint rumbling noise can be
|
||||
heard in the distance.
|
||||
#263
|
||||
The path forks here. The left fork leads northeast. A dull rumbling
|
||||
seems to get louder in that direction. The right fork leads southeast
|
||||
down a gentle slope. The main corridor enters from the west.
|
||||
#264
|
||||
You are walking along a gently sloping north/south passage lined with
|
||||
oddly shaped limestone formations.
|
||||
#265
|
||||
The walls are quite warm here. From the north can be heard a steady
|
||||
roar, so loud that the entire cave seems to be trembling. Another
|
||||
passage leads south, and a low crawl goes east.
|
||||
#266
|
||||
You are in a small chamber filled with large boulders. The walls are
|
||||
very warm, causing the air in the room to be almost stifling from the
|
||||
heat. The only exit is a crawl heading west, through which is coming
|
||||
a low rumbling.
|
||||
#267
|
||||
You are on the edge of a breath-taking view. Far below you is an
|
||||
active volcano, from which great gouts of molten lava come surging
|
||||
out, cascading back down into the depths. The glowing rock fills the
|
||||
farthest reaches of the cavern with a blood-red glare, giving every-
|
||||
thing an eerie, macabre appearance. The air is filled with flickering
|
||||
sparks of ash and a heavy smell of brimstone. The walls are hot to
|
||||
the touch, and the thundering of the volcano drowns out all other
|
||||
sounds. Embedded in the jagged roof far overhead are myriad twisted
|
||||
formations composed of pure white alabaster, which scatter the murky
|
||||
light into sinister apparitions upon the walls. To one side is a deep
|
||||
gorge, filled with a bizarre chaos of tortured rock which seems to
|
||||
have been crafted by the devil himself. An immense river of fire
|
||||
crashes out from the depths of the volcano, burns its way through the
|
||||
gorge, and plummets into a bottomless pit far off to your left. To
|
||||
the right, an immense geyser of blistering steam erupts continuously
|
||||
from a barren island in the center of a sulfurous lake, which bubbles
|
||||
ominously. The far right wall is aflame with an incandescence of its
|
||||
own, which lends an additional infernal splendor to the already
|
||||
hellish scene. A dark, foreboding passage exits to the south.
|
||||
#268
|
||||
You are standing at the entrance to a large, barren room. A sign
|
||||
posted above the entrance reads: "Caution! Bear in room!"
|
||||
#269
|
||||
You are inside a barren room. The center of the room is completely
|
||||
empty except for some dust. Marks in the dust lead away toward the
|
||||
far end of the room. The only exit is the way you came in.
|
||||
#270
|
||||
You are in a long, narrow corridor stretching out of sight to the
|
||||
west. At the eastern end is a hole through which you can see a
|
||||
profusion of leaves.
|
||||
#271
|
||||
You are in the giant room. The ceiling here is too high up for your
|
||||
lamp to show it. Cavernous passages lead east, north, and south. On
|
||||
the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic].
|
||||
#272
|
||||
The passage here is blocked by a recent cavein.
|
||||
#273
|
||||
You are at one end of an immense north/south passage.
|
||||
#274
|
||||
You are in a magnificent cavern with a rushing stream, which cascades
|
||||
over a sparkling waterfall into a roaring whirlpool which disappears
|
||||
through a hole in the floor. Passages exit to the south and west.
|
||||
#275
|
||||
You are at the top of a steep incline above a large room. You could
|
||||
climb down here, but you would not be able to climb up. There is a
|
||||
passage leading back to the north.
|
||||
#276
|
||||
You are in a secret N/S canyon above a sizable passage.
|
||||
#277
|
||||
A large stalactite extends from the roof and almost reaches the floor
|
||||
below. You could climb down it, and jump from it to the floor, but
|
||||
having done so you would be unable to reach it to climb back up.
|
||||
|
||||
The maze continues at this level.
|
||||
#278
|
||||
You are in a secret canyon at a junction of three canyons, bearing
|
||||
north, south, and SE. The north one is as tall as the other two
|
||||
combined.
|
||||
#279
|
||||
You are in a secret N/S canyon above a large room.
|
||||
#280
|
||||
You are in a north/south canyon about 25 feet across. The floor is
|
||||
covered by white mist seeping in from the north. The walls extend
|
||||
upward for well over 100 feet. Suspended from some unseen point far
|
||||
above you, an enormous two-sided mirror is hanging parallel to and
|
||||
midway between the canyon walls. (The mirror is obviously provided
|
||||
for the use of the dwarves, who as you know, are extremely vain.) A
|
||||
small window can be seen in either wall, some fifty feet up.
|
||||
#281
|
||||
You are at the edge of a large underground reservoir. An opaque cloud
|
||||
of white mist fills the room and rises rapidly upward. The lake is
|
||||
fed by a stream, which tumbles out of a hole in the wall about 10 feet
|
||||
overhead and splashes noisily into the water somewhere within the
|
||||
mist. The only passage goes back toward the south.
|
||||
#282
|
||||
You are in a secret canyon which exits to the north and east.
|
||||
#283
|
||||
You're in the secret E/W canyon above a tight canyon.
|
||||
#284
|
||||
You are at a wide place in a very tight N/S canyon.
|
||||
#285
|
||||
The canyon here becomes too tight to go on.
|
||||
#286
|
||||
You are in a tall E/W canyon. A low tight crawl goes 3 feet north and
|
||||
seems to open up.
|
||||
#287
|
||||
The canyon runs into a maze of boulders -- dead end.
|
||||
#288
|
||||
You are in a maze of twisty little passages, all alike.
|
||||
#298
|
||||
You are near a large pit beside a large stalactite in the maze.
|
||||
The stalactite is about 30 feet long, allowing you to descend
|
||||
it. However, due to the smoothness of the stalactite you will
|
||||
probably be unable to climb back up again.
|
||||
#299
|
||||
The threatening little dwarf pulls out a sharp, nasty knife but
|
||||
suddenly freezes.
|
||||
|
||||
The burly troll notices the little dwarf and appears to become very angry.
|
||||
|
||||
The little dwarf frantically looks from side to side, the burly troll
|
||||
takes a sudden step towards the little dwarf.
|
||||
|
||||
The little dwarf makes an odd squeal, steps back and trips on an outcropping,
|
||||
falling with a soft *thud* before scrambling out of sight in a cloud of dust.
|
||||
#300
|
||||
Dead end.
|
||||
#301
|
||||
Welcome to adventure.! Would you like instructions?
|
||||
#302
|
||||
Somewhere nearby is Colossal Cave, where others have found fortunes in
|
||||
treasure and gold, though it is rumored that some who enter are never
|
||||
seen again. Magic is said to work in the cave. I will be your eyes
|
||||
and hands. Direct me with commands such as get, take, or look. Since you
|
||||
need to move around, you must usually enter compass directions
|
||||
(N,NE,E,SE,S,SW,W,NW or up or down), although you may sometimes use
|
||||
more vague verbs with with to move. I know of several objects in this
|
||||
game, such as a lamp and a bottle. I also know of special objects in the
|
||||
cave. Some of these objects have side effects, ie there is a rod in the
|
||||
cave that scares a little bird.
|
||||
-----------------------------------------------------------------
|
||||
The object of this game is to gather as many treasures as you can and
|
||||
put them back in the house. You also run the risk of getting robbed or
|
||||
killed by some rather unfriendly inhabitants of the cave.
|
||||
----------------------------------------------------------------
|
||||
There are some useful commands that you should know about:
|
||||
--- Brief, Long, and Short - these commands control the amount of
|
||||
--- detail you get in descriptions.
|
||||
--- Look (or L) - gives a detailed description of your surroundings.
|
||||
--- Inventory (or I) tells you what you are carrying.
|
||||
--- Quit, Stop or End - these are self-explanatory.
|
||||
--- Save - by typing this, you may save your game and continue it at
|
||||
--- a later time.
|
||||
--- Continue - lets you continue an old game.
|
||||
--- Score - tells you hw well you're doing.
|
||||
|
||||
Other helpful functions include:
|
||||
* multiple commands on one input line, ie:
|
||||
"Go south, get car, keys, Start car." (example only)
|
||||
--------------------------------------------------------------
|
||||
"Adventure" is a version of the game created by Willy Crowther and
|
||||
Dan Woods at M.I.T.
|
||||
--------------------------------------------------------------
|
||||
#303
|
||||
I'm sorry, but the magazine is written in Dwarvish.
|
||||
#
|
||||
#
|
||||
#
|
||||
184
builtin_apps/PyBasic/examples/AMESSAGE.IDX
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
1,0
|
||||
2,1
|
||||
3,235
|
||||
4,307
|
||||
5,365
|
||||
6,400
|
||||
7,531
|
||||
8,601
|
||||
9,673
|
||||
10,715
|
||||
11,742
|
||||
12,771
|
||||
13,864
|
||||
14,914
|
||||
15,960
|
||||
16,987
|
||||
17,1029
|
||||
18,1219
|
||||
19,1245
|
||||
20,1350
|
||||
21,1369
|
||||
22,1397
|
||||
23,1430
|
||||
24,1488
|
||||
25,1570
|
||||
26,1713
|
||||
27,1858
|
||||
28,1928
|
||||
29,2074
|
||||
30,2137
|
||||
31,2224
|
||||
32,2288
|
||||
33,2334
|
||||
34,2564
|
||||
35,2632
|
||||
36,2657
|
||||
37,2684
|
||||
38,2797
|
||||
39,2828
|
||||
40,2874
|
||||
41,2931
|
||||
42,2966
|
||||
43,3029
|
||||
44,3098
|
||||
45,3159
|
||||
46,3236
|
||||
47,3280
|
||||
48,3357
|
||||
49,3456
|
||||
50,3531
|
||||
51,3585
|
||||
52,3699
|
||||
53,3790
|
||||
54,3861
|
||||
55,3944
|
||||
56,4045
|
||||
57,4109
|
||||
58,4171
|
||||
59,4253
|
||||
60,4347
|
||||
61,6
|
||||
62,4584
|
||||
63,4672
|
||||
64,4737
|
||||
65,4815
|
||||
66,4873
|
||||
67,4930
|
||||
68,4987
|
||||
69,5022
|
||||
70,5131
|
||||
71,5186
|
||||
72,5225
|
||||
73,5285
|
||||
74,5320
|
||||
75,5360
|
||||
76,5535
|
||||
77,5744
|
||||
78,5880
|
||||
79,6048
|
||||
80,6092
|
||||
81,6213
|
||||
200,6260
|
||||
205,6315
|
||||
206,6464
|
||||
207,6626
|
||||
208,6693
|
||||
209,6779
|
||||
210,6909
|
||||
211,7082
|
||||
212,7210
|
||||
213,7320
|
||||
214,7565
|
||||
215,7620
|
||||
216,7807
|
||||
217,7979
|
||||
218,8377
|
||||
219,8484
|
||||
220,8628
|
||||
221,8697
|
||||
222,8861
|
||||
223,8948
|
||||
224,8990
|
||||
225,9103
|
||||
226,9203
|
||||
227,9383
|
||||
228,9935
|
||||
229,9991
|
||||
230,10168
|
||||
231,10287
|
||||
232,10358
|
||||
233,10374
|
||||
234,10516
|
||||
235,10598
|
||||
236,10708
|
||||
237,10854
|
||||
238,11071
|
||||
239,11344
|
||||
240,11414
|
||||
241,11685
|
||||
242,11834
|
||||
243,11901
|
||||
244,11976
|
||||
245,12108
|
||||
246,12271
|
||||
247,12411
|
||||
248,12753
|
||||
249,12879
|
||||
250,13004
|
||||
251,13133
|
||||
252,13436
|
||||
253,13640
|
||||
254,13708
|
||||
255,13792
|
||||
256,13815
|
||||
257,14066
|
||||
258,14275
|
||||
259,14412
|
||||
260,14597
|
||||
261,14882
|
||||
262,15113
|
||||
263,15212
|
||||
264,15424
|
||||
265,15537
|
||||
266,15730
|
||||
267,15966
|
||||
268,17284
|
||||
269,17418
|
||||
270,17623
|
||||
271,17783
|
||||
272,17999
|
||||
273,18054
|
||||
274,18115
|
||||
275,18330
|
||||
276,18511
|
||||
277,18574
|
||||
278,18823
|
||||
279,18974
|
||||
280,19032
|
||||
281,19514
|
||||
282,19853
|
||||
283,19922
|
||||
284,19983
|
||||
285,20042
|
||||
286,20093
|
||||
287,20190
|
||||
288,20250
|
||||
298,20313
|
||||
299,20554
|
||||
300,21003
|
||||
301,21020
|
||||
302,21080
|
||||
303,22968
|
||||
-999,-999
|
||||
30
|
||||
54
|
||||
86
|
||||
140
|
||||
189
|
||||
4410
|
||||
4438
|
||||
4456
|
||||
4512
|
||||
4536
|
||||
-999
|
||||
100
builtin_apps/PyBasic/examples/AMOVING
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
3,0,6,0,4,0,2,0,0,0
|
||||
1,0,8,0,2,0,4,0,0,0
|
||||
2,0,1,0,4,0,7,0,0,0
|
||||
1,0,3,0,5,0,2,0,0,0
|
||||
2,0,6,0,4,0,1,0,0,0
|
||||
2,0,7,0,8,0,5,0,0,0
|
||||
0,0,0,0,0,0,6,0,0,0
|
||||
6,0,3,0,9,0,4,0,0,0
|
||||
8,0,3,0,10,0,4,0,0,0
|
||||
9,0,0,0,11,0,0,0,0,11
|
||||
0,0,10,0,0,0,12,0,10,0
|
||||
0,0,11,0,0,0,13,0,0,0
|
||||
0,0,12,0,0,0,14,0,14,0
|
||||
0,0,13,0,0,0,15,0,15,13
|
||||
0,0,14,0,0,0,16,0,0,0
|
||||
0,0,15,0,0,0,0,0,0,17
|
||||
22,0,0,0,18,0,19,0,16,22
|
||||
17,0,0,0,0,0,0,0,0,0
|
||||
0,0,17,0,0,0,20,0,0,0
|
||||
0,0,19,0,0,0,21,0,0,0
|
||||
20,0,20,0,88,0,29,0,0,0
|
||||
25,0,17,0,23,83,24,0,17,0
|
||||
22,0,0,0,0,0,0,0,0,0
|
||||
0,0,22,0,0,0,31,0,31,0
|
||||
26,0,0,0,22,0,0,0,0,33
|
||||
0,0,28,0,25,0,27,0,0,0
|
||||
0,0,26,0,0,0,0,0,0,0
|
||||
0,0,0,0,0,26,0,0,0,0
|
||||
31,0,21,0,0,0,30,0,21,31
|
||||
31,0,29,0,94,0,0,0,0,0
|
||||
32,0,24,0,30,0,29,0,0,0
|
||||
31,0,0,0,0,0,0,0,0,0
|
||||
0,0,34,0,0,0,36,0,25,0
|
||||
0,0,0,0,0,0,33,0,0,35
|
||||
0,0,0,0,0,0,0,0,34,0
|
||||
0,0,33,0,0,0,0,0,25,37
|
||||
40,0,38,0,0,0,44,0,36,0
|
||||
0,0,39,0,0,0,37,0,37,0
|
||||
255,255,255,255,255,255,255,255,255,255
|
||||
0,0,0,0,37,0,0,0,41,42
|
||||
0,0,0,0,0,0,0,0,0,43
|
||||
0,0,0,0,0,0,0,0,40,43
|
||||
0,0,0,0,0,0,0,0,42,0
|
||||
255,0,37,0,255,0,45,0,255,255
|
||||
0,44,46,0,0,0,47,52,0,0
|
||||
0,0,0,0,0,0,45,0,0,0
|
||||
0,0,45,0,0,0,48,0,0,49
|
||||
0,0,47,0,0,0,51,0,0,50
|
||||
0,0,0,0,0,0,0,0,47,0
|
||||
0,0,0,0,0,0,0,0,48,0
|
||||
44,0,0,0,48,0,0,0,79,0
|
||||
56,0,0,45,0,0,53,0,0,0
|
||||
55,0,0,52,0,54,0,0,0,0
|
||||
0,0,0,0,0,0,0,0,60,53
|
||||
0,0,0,0,53,0,0,0,0,0
|
||||
0,0,0,0,52,0,57,0,0,0
|
||||
0,0,58,0,0,0,0,56,0,0
|
||||
0,59,0,0,0,0,57,0,0,0
|
||||
0,0,0,0,58,0,0,0,0,0
|
||||
0,61,0,0,0,54,0,0,0,54
|
||||
0,62,0,0,0,60,0,0,0,0
|
||||
0,0,63,0,0,0,61,0,0,0
|
||||
0,65,0,64,0,0,62,0,0,0
|
||||
63,0,0,0,68,0,0,0,0,0
|
||||
67,0,66,0,63,0,0,0,0,0
|
||||
0,0,0,0,0,0,65,0,0,0
|
||||
0,0,0,0,65,0,0,0,0,0
|
||||
0,0,69,0,0,0,64,0,0,0
|
||||
0,0,0,0,0,0,68,0,0,0
|
||||
0,0,50,0,0,0,71,0,0,50
|
||||
73,0,70,0,72,0,0,0,0,0
|
||||
0,71,0,0,0,0,0,0,0,0
|
||||
74,0,0,0,71,0,0,0,0,0
|
||||
0,0,0,0,73,0,75,0,0,0
|
||||
74,0,0,0,0,0,0,0,0,53
|
||||
78,0,0,0,77,0,0,0,0,45
|
||||
76,0,0,0,0,0,0,0,0,93
|
||||
27,0,0,44,76,0,0,0,0,0
|
||||
80,0,0,0,82,0,0,0,0,51
|
||||
81,0,0,0,79,0,0,0,0,0
|
||||
0,0,0,0,80,0,0,0,0,0
|
||||
79,0,83,0,0,0,0,0,0,0
|
||||
0,0,22,0,0,0,82,0,0,84
|
||||
86,0,0,0,85,0,0,0,0,0
|
||||
84,0,0,0,0,0,0,0,0,0
|
||||
45,0,84,0,0,0,87,0,0,0
|
||||
0,0,0,86,0,0,0,0,0,0
|
||||
89,82,91,95,90,83,92,91,95,94
|
||||
95,96,91,90,89,88,91,95,93,97
|
||||
92,92,91,93,88,97,88,96,99,87
|
||||
91,89,94,90,93,95,89,92,0,0
|
||||
95,97,93,96,94,95,21,92,0,0
|
||||
90,96,92,94,88,30,92,90,0,0
|
||||
98,89,90,91,89,93,95,97,0,0
|
||||
93,94,96,92,97,95,94,92,0,0
|
||||
92,99,97,96,89,94,90,90,91,92
|
||||
98,89,92,91,90,93,94,95,96,98
|
||||
95,90,93,91,94,92,90,99,98,15
|
||||
88,89,93,97,100,89,96,95,0,0
|
||||
0,0,0,0,99,0,0,0,0,0
|
||||
585
builtin_apps/PyBasic/examples/Pneuma.bas
Normal file
|
|
@ -0,0 +1,585 @@
|
|||
10 REM Pneuma - A space adventure
|
||||
20 REM ========== backstory and instructions ==========
|
||||
30 PRINT "********************************" : PRINT
|
||||
35 PRINT " Pneuma - A space adventure" : PRINT
|
||||
40 PRINT "********************************": PRINT
|
||||
45 PRINT "To get instructions, type 'help'"
|
||||
50 PRINT
|
||||
60 PRINT "You wake up in your bunk, in the sleeping quarters of the starship Pneuma. You can't"
|
||||
65 PRINT "remember much. You went to bed feeling sick and after a feverish few hours tossing and"
|
||||
70 PRINT "turning, feeling like you were burning up, you eventually fell asleep." : PRINT
|
||||
75 PRINT "Now, your sheets and night clothes are damp with sweat, and you have a raging thirst."
|
||||
80 PRINT "You have a sore throat and the mother of all headaches, like your brain has been boiling"
|
||||
85 PRINT "in your skull. In fact, you're no longer sure exactly where you are ... you seem to be"
|
||||
90 PRINT "suffering from some sort of amnesia ...": PRINT
|
||||
95 REM set up environment
|
||||
100 GOSUB 2700
|
||||
500 REM setup room descriptions
|
||||
510 GOSUB 3000
|
||||
520 REM setup up interative descriptions
|
||||
530 GOSUB 5000
|
||||
540 REM setup dialogue
|
||||
550 GOSUB 7000
|
||||
700 REM ========== main loop ==========
|
||||
701 IF WPL <> 99 THEN GOSUB 8400 : REM wraith-hound movement
|
||||
702 REM show room details
|
||||
703 PRINT "You are in the " ; LO$ ( PL ) : PRINT
|
||||
705 GOSUB 4010 : REM print room description
|
||||
706 GOSUB 8000 : REM tracker info if carried
|
||||
707 IF WPL <> 99 THEN GOSUB 8200 : REM wraith-hound proximity check
|
||||
709 IF WPL = PL THEN GOSUB 11000 : REM fight wraith-hound
|
||||
710 INPUT "What now? " ; I$
|
||||
715 PRINT
|
||||
716 MOVE = 1
|
||||
720 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "get " THEN MOVE = 0 : GOSUB 1400
|
||||
730 IF LEFT$ ( LOWER$ ( I$ ) , 5 ) = "take " THEN MOVE = 0 : GOSUB 1700
|
||||
740 IF LEFT$ ( LOWER$ ( I$ ) , 5 ) = "drop " THEN MOVE = 0 : GOSUB 2000
|
||||
750 IF LEFT$ ( LOWER$ ( I$ ) , 8 ) = "examine " THEN MOVE = 0 : GOSUB 2300
|
||||
760 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "look" THEN MOVE = 0 : GOSUB 4010
|
||||
765 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "help" THEN MOVE = 0 : GOSUB 4130
|
||||
767 IF LEFT$ ( LOWER$ ( I$ ) , 4 ) = "use " THEN MOVE = 0 : GOSUB 9000
|
||||
770 IF LOWER$ ( I$ ) = "i" OR LOWER$ ( I$ ) = "inventory" THEN MOVE = 0 : GOSUB 1000
|
||||
775 IF LEFT$ ( LOWER$ ( I$ ) , 8 ) = "talk to " THEN MOVE = 0 : GOSUB 2630
|
||||
780 IF LEFT$ ( LOWER$ ( I$ ) , 1 ) = "q" THEN GOSUB 2600
|
||||
785 IF LEFT$ ( LOWER$ ( I$ ) , 3 ) = "go " THEN GOSUB 1100
|
||||
790 IF LOWER$ ( I$ ) = "f" OR LOWER$ ( I$ ) = "forward" THEN GOSUB 1200
|
||||
800 IF LOWER$ ( I$ ) = "a" OR LOWER$ ( I$ ) = "aft" THEN GOSUB 1200
|
||||
810 IF LOWER$ ( I$ ) = "p" OR LOWER$ ( I$ ) = "port" THEN GOSUB 1200
|
||||
820 IF LOWER$ ( I$ ) = "s" OR LOWER$ ( I$ ) = "starboard" THEN GOSUB 1200
|
||||
830 IF LOWER$ ( I$ ) = "u" OR LOWER$ ( I$ ) = "up" THEN GOSUB 1200
|
||||
840 IF LOWER$ ( I$ ) = "d" OR LOWER$ ( I$ ) = "down" THEN GOSUB 1200
|
||||
895 IF MOVE = 1 THEN GOTO 700 ELSE GOTO 710
|
||||
900 STOP
|
||||
995 REM ========== actions ==========
|
||||
1000 REM list the player's inventory
|
||||
1005 PRINT "You have the following items:" : PRINT
|
||||
1010 FOR I = 0 TO OC - 1
|
||||
1020 IF OL ( I ) = 0 THEN PRINT OB$ ( I )
|
||||
1030 NEXT I
|
||||
1035 PRINT
|
||||
1040 RETURN
|
||||
1100 REM fully written out move (e.g. 'go aft')
|
||||
1110 D$ = MID$ ( LOWER$(I$) , 4 , 1 )
|
||||
1120 GOSUB 1300
|
||||
1130 RETURN
|
||||
1200 REM abbreviated move (e.g. 'a' or 'aft')
|
||||
1210 D$ = LOWER$( I$ )
|
||||
1220 GOSUB 1300
|
||||
1230 RETURN
|
||||
1300 REM go to the player's new location (PL)
|
||||
1310 IF D$ = "f" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 1 , 2 ) )
|
||||
1320 IF D$ = "a" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 3 , 2 ) )
|
||||
1330 IF D$ = "p" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 5 , 2 ) )
|
||||
1340 IF D$ = "s" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 7 , 2 ) )
|
||||
1345 IF D$ = "u" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 9 , 2 ) )
|
||||
1350 IF D$ = "d" THEN NPL = VAL ( MID$ ( EX$ ( PL ) , 11 , 2 ) )
|
||||
1355 IF NPL = 0 THEN PRINT "You can't go that way." : PRINT ELSE PL = NPL
|
||||
1360 RETURN
|
||||
1400 REM get command
|
||||
1405 F=-1: R$=""
|
||||
1410 R$ = MID$(LOWER$(I$), 5) : REM R$ is the requested object
|
||||
1420 REM get the object ID
|
||||
1430 FOR I= 0 TO OC-1
|
||||
1440 IF OB$(I) = R$ THEN F=I : REM object exists
|
||||
1450 NEXT I
|
||||
1460 REM can't find the item?
|
||||
1470 IF F=-1 THEN PRINT "You can't take that." : PRINT : RETURN
|
||||
1480 IF OL(F) <> PL THEN PRINT "That item doesn't appear to be around here." : PRINT : RETURN
|
||||
1490 IF OL(F)=0 THEN PRINT "You already have that item." : PRINT: RETURN
|
||||
1520 OL(F)=0 : REM add the item to the inventory
|
||||
1530 PRINT "You've picked up ";OB$(F); "." : PRINT
|
||||
1540 RETURN
|
||||
1700 REM take command
|
||||
1710 F=-1: R$=""
|
||||
1720 R$ = MID$(LOWER$(I$), 6) : REM R$ is the requested object
|
||||
1730 GOTO 1420 : REM use the same logic as the get command
|
||||
2000 REM drop command
|
||||
2010 F=-1: R$=""
|
||||
2020 R$ = MID$(LOWER$(I$), 6) : REM R$ is the requested object
|
||||
2030 FOR I= 0 TO OC-1
|
||||
2040 IF OB$(I) = R$ THEN F=I : REM object exists
|
||||
2050 NEXT I
|
||||
2060 REM can't find it?
|
||||
2070 IF F=-1 THEN PRINT "You don't have that." : PRINT: RETURN
|
||||
2080 IF OL(F) <> 0 THEN PRINT "You aren't carrying that." : PRINT: RETURN
|
||||
2090 OL(F) = PL : PRINT "You've dropped ";OB$(F); ".": PRINT: REM add the item to the current room
|
||||
2095 IF F = SUIT THEN SUIT_WORN = 0
|
||||
2110 RETURN
|
||||
2300 REM examine command
|
||||
2310 F=-1 : R$=""
|
||||
2320 R$ = MID$(LOWER$(I$), 9) : REM R$ is the object to examine
|
||||
2330 FOR I = 0 TO IC-1
|
||||
2340 IF IO$(I) = R$ THEN F=I : REM object exists
|
||||
2350 NEXT I
|
||||
2360 REM can't find it?
|
||||
2370 IF F=-1 THEN PRINT "You can't examine that." : PRINT : RETURN
|
||||
2380 IF IL(F) <> PL THEN PRINT "There isn't one of these here." : PRINT : RETURN
|
||||
2390 GOSUB 6000 : REM print result of examination
|
||||
2400 RETURN
|
||||
2600 REM quit command
|
||||
2610 PRINT "Farewell spacefarer ..."
|
||||
2620 STOP
|
||||
2630 REM talk to command
|
||||
2635 F=-1 : R$ = ""
|
||||
2640 R$ = MID$(LOWER$(I$), 9) : REM R$ is the person to talk to
|
||||
2645 FOR I = 0 TO PC-1
|
||||
2650 IF P$(I) = R$ THEN F=I : REM person exists
|
||||
2655 NEXT I
|
||||
2660 REM can't find them?
|
||||
2665 IF F=-1 THEN PRINT "You've not met them." : PRINT : RETURN
|
||||
2670 IF PLOC(F) <> PL THEN PRINT "They aren't here." : PRINT : RETURN
|
||||
2675 GOSUB 7500 : REM print dialogue
|
||||
2695 RETURN
|
||||
2700 REM ========== set up environment ===========
|
||||
2705 RC = 18 : REM room count
|
||||
2710 DIM LO$ ( RC )
|
||||
2715 INV = 0 : LO$ ( INV ) = "Inventory"
|
||||
2720 GAL = 1 : LO$ ( GAL ) = "Galley"
|
||||
2725 REC = 2 : LO$ ( REC ) = "Recreation/Dining Room"
|
||||
2730 ARM = 3 : LO$ ( ARM ) = "Armoury"
|
||||
2735 BDG = 4 : LO$ ( BDG ) = "Bridge"
|
||||
2740 SLP = 5 : LO$ ( SLP ) = "Sleeping Quarters"
|
||||
2745 MED = 6 : LO$ ( MED ) = "Medical Centre"
|
||||
2750 GYM = 7 : LO$ ( GYM ) = "Gymnasium"
|
||||
2755 LAC = 8 : LO$ ( LAC ) = "Lower Aft Corridor"
|
||||
2760 ENG = 9 : LO$ ( ENG ) = "Engine Room"
|
||||
2765 STO = 10 : LO$ ( STO ) = "Storeroom"
|
||||
2770 MEN = 11 : LO$ ( MEN ) = "Menagerie"
|
||||
2775 LAB = 12 : LO$ ( LAB ) = "Laboratory"
|
||||
2780 LFC = 13 : LO$ ( LFC ) = "Lower Forward Corridor"
|
||||
2785 POD = 14 : LO$ ( POD ) = "Pod Bay"
|
||||
2790 AMC = 15 : LO$ ( AMC ) = "Aft Main Corridor"
|
||||
2795 MMC = 16 : LO$ ( MMC ) = "Mid Main Corridor"
|
||||
2800 FMC = 17 : LO$ ( FMC ) = "Forward Main Corridor"
|
||||
2805 REM encoded room exits, two digits per direction f, a, p, s, u, d
|
||||
2810 DIM EX$ ( RC )
|
||||
2815 EX$ ( GAL ) = "020000150008"
|
||||
2820 EX$ ( REC ) = "000100160000"
|
||||
2825 EX$ ( ARM ) = "000000170000"
|
||||
2830 EX$ ( BDG ) = "001700000000"
|
||||
2835 EX$ ( SLP ) = "000015000000"
|
||||
2840 EX$ ( MED ) = "070016000000"
|
||||
2845 EX$ ( GYM ) = "000617000013"
|
||||
2850 EX$ ( LAC ) = "100000090100"
|
||||
2855 EX$ ( ENG ) = "000008000000"
|
||||
2860 EX$ ( STO ) = "120800110000"
|
||||
2865 EX$ ( MEN ) = "000010000000"
|
||||
2870 EX$ ( LAB ) = "001000130000"
|
||||
2875 EX$ ( LFC ) = "140012000700"
|
||||
2880 EX$ ( POD ) = "001300000000"
|
||||
2890 EX$ ( AMC ) = "160001050000"
|
||||
2895 EX$ ( MMC ) = "171502060000"
|
||||
2900 EX$ ( FMC ) = "041603070000"
|
||||
2905 OC = 5 : REM object count
|
||||
2910 DIM OB$ ( OC )
|
||||
2915 PULSE = 0 : OB$ ( PULSE ) = "pulse rifle"
|
||||
2920 SUIT = 1 : OB$ ( SUIT ) = "space suit"
|
||||
2922 KNIFE = 2 : OB$ ( KNIFE ) = "knife"
|
||||
2924 TRACKER = 3 : OB$ ( TRACKER ) = "tracker"
|
||||
2926 SYRINGE = 4 : OB$ ( SYRINGE ) = "syringe"
|
||||
2930 REM object locations
|
||||
2932 REM location 0 = player's inventory
|
||||
2934 DIM OL ( OC )
|
||||
2936 OL ( PULSE ) = ARM
|
||||
2937 OL ( SUIT ) = POD
|
||||
2939 OL ( KNIFE ) = GAL
|
||||
2940 OL ( TRACKER ) = GYM
|
||||
2941 OL ( SYRINGE ) = MED
|
||||
2942 IC = 5 : REM interactive object count
|
||||
2944 DIM IO$ (IC)
|
||||
2946 MEDLOG = 0 : IO$ ( MEDLOG ) = "medical log"
|
||||
2948 PORTHOLE = 1 : IO$ (PORTHOLE) = "porthole"
|
||||
2950 CONSOLE = 2 : IO$(CONSOLE) = "console"
|
||||
2952 ENGINE = 3 : IO$(ENGINE)= "engine control"
|
||||
2954 TERMINAL = 4 : IO$(TERMINAL) = "library terminal"
|
||||
2960 REM interative object locations
|
||||
2962 DIM IL ( IC )
|
||||
2964 IL ( MEDLOG) = MED
|
||||
2966 IL (PORTHOLE) = POD
|
||||
2968 IL (CONSOLE) = BDG
|
||||
2970 IL (ENGINE) = ENG
|
||||
2971 IL (TERMINAL) = REC
|
||||
2972 PC = 3 : REM person count
|
||||
2974 DIM P$ ( PC )
|
||||
2976 CHEF = 0 : P$ ( CHEF ) = "chef"
|
||||
2978 RUNNER = 1 : P$ ( RUNNER ) = "runner"
|
||||
2980 PILOT = 2 : P$ ( PILOT ) = "pilot"
|
||||
2981 REM person locations
|
||||
2983 DIM PLOC ( PC )
|
||||
2985 PLOC ( CHEF ) = GAL
|
||||
2987 PLOC ( RUNNER ) = GYM
|
||||
2989 PLOC ( PILOT ) = BDG
|
||||
2990 PL = SLP : REM initial player location
|
||||
2992 WPL = MEN : REM wraith-hound initial location
|
||||
2994 SUIT_WORN = 0 : REM is space suit worn?
|
||||
2995 SHUTDOWN = 0 : REM are engines shut down?
|
||||
2999 RETURN
|
||||
3000 REM ========== room descriptions ==========
|
||||
3010 DIM RD$ ( RC, 5 )
|
||||
3015 REM inventory
|
||||
3020 DATA "", "", "", "", ""
|
||||
3025 REM galley
|
||||
3030 DATA "The galley contains gleaming, stainless steel cupboards along the aft wall. A food"
|
||||
3040 DATA "preparation surface is on the port wall, currently covered in rotting food. A chef"
|
||||
3050 DATA "stands at the work surface, methodically chopping food even though everything has"
|
||||
3060 DATA "already been thoroughly diced. There are doors in the starboard and forward walls and"
|
||||
3070 DATA "a stairway leads downwards in the far corner."
|
||||
3075 REM recreation room
|
||||
3080 DATA "Space is clearly at a premium in this ship. The room doubles as both a dining and"
|
||||
3090 DATA "recreation area. Long tables for dining are located on the port side, while couches"
|
||||
3100 DATA "and low tables are scattered around the remaining space. A library terminal is switched"
|
||||
3105 DATA "on in the corner. There are doors in the aft and starboard walls.", ""
|
||||
3120 REM armoury
|
||||
3130 DATA "Locked cabinets line the starboard wall. Each cabinet has a prominently displayed"
|
||||
3140 DATA "notice on its door reading 'Weapons to be removed only when authorised by the Chief"
|
||||
3150 DATA "Security Officer'. However, the door to one cupboard has been prized open, it is"
|
||||
3160 DATA "warped and bent. This cupboard appears to be empty. The only exit is a starboard door.", ""
|
||||
3170 REM bridge
|
||||
3180 DATA "The bridge is the heart of the ship. A vast array of glowing screens and switches fill"
|
||||
3190 DATA "every surface. On the screens are complex graphics providing detailed information about"
|
||||
3200 DATA "the status of every system on the ship. Many of them are showing red warning symbols."
|
||||
3210 DATA "There is a console directly in front of you, a pilot gripping the throttle."
|
||||
3220 DATA "An aft exit leads back into the main corridor."
|
||||
3225 REM sleeping quarters
|
||||
3230 DATA "The sleeping quarters is filled with bunks, one up, one down. Several of the bunks"
|
||||
3240 DATA "contain sleeping forms, some gently shoring. The room has a partition to separate"
|
||||
3250 DATA "male and female bunks. Against the forward wall are two corresponding sets of heads."
|
||||
3260 DATA "The room is messy, with discarded personal items everywhere ... on bunks, on the floor."
|
||||
3270 DATA "There is a door in the port wall."
|
||||
3275 REM medical centre
|
||||
3280 DATA "The medical centre looks relatively normal, but there is evidence of discarded items"
|
||||
3290 DATA "lying around the room. Blood filled syringes are scattered on a workbench, as well as"
|
||||
3300 DATA "some bloodied bandanges. The words 'I'm losing myself' are scrawled messily in blood on"
|
||||
3310 DATA "one wall. In the corner you can see a terminal. On the terminal screen is a portion"
|
||||
3320 DATA "of the medical log. Exits lead port and forward."
|
||||
3325 REM gymnasium
|
||||
3330 DATA "The gymnasium is full of exercise equipment. A female runner is sprinting furiously on a"
|
||||
3340 DATA "treadmill. She looks exhausted and emaciated, but she keeps running at top speed, almost at"
|
||||
3350 DATA "a sprint. Her eyes remain fixed on the treadmill console. There are aft and port exits,"
|
||||
3360 DATA "as well as a stairwell leading to the lower deck in the far corner.", ""
|
||||
3370 REM lower aft corridor
|
||||
3380 DATA "This is a featureless, utilitarian corridor. A stairwell leads upwards. There are also"
|
||||
3390 DATA "exits leading starboard and forward.", "", "", ""
|
||||
3400 REM engine room
|
||||
3430 DATA "The engine room is characterised by a continual rumble, as though incredible energies are"
|
||||
3440 DATA "barely being contained. There is an engine control in the far corner, festooned with"
|
||||
3450 DATA "switches and engine readouts. A single exit leads out into the corridor.", "", ""
|
||||
3460 REM storeroom
|
||||
3480 DATA "The storeroom is full of crates, most neatly stacked, but with some scattered across the"
|
||||
3490 DATA "floor, their contents spilling out. Along the port wall is a door marked 'Test specimens'."
|
||||
3500 DATA "From behind the door, strange animal noises are audible ... snuffling sounds and the"
|
||||
3510 DATA "occasional primate shriek. A dead man wearing a scuffed and torn lab coat is lying face down"
|
||||
3520 DATA "in front of the specimen door. Two other exits lead forward and aft."
|
||||
3525 REM menagerie
|
||||
3530 DATA "The room is a hellhole. Cages stand open, while various animals roam about: chimpanzees,"
|
||||
3540 DATA "dogs, and rats. Some of the rats are dead, having been savaged and eviscerated. The floor"
|
||||
3550 DATA "and walls are smeared with animal faeces, and the smell is almost overpowering. A capsule,"
|
||||
3552 DATA "its door ajar, is marked 'BIOWEAPON CONTAINMENT'. The capsule is empty."
|
||||
3555 DATA "A single door to port leads back into the storeroom."
|
||||
3560 REM laboratory
|
||||
3570 DATA "The laboratory is full of scientific equipment, chemical glassware, electronic analysers,"
|
||||
3580 DATA "fume cupboards, and two couches. The place looks disorded, like the rest of the ship, the"
|
||||
3590 DATA "result of frenetic activity. A number of experiments seem to be in progress, with logbooks"
|
||||
3600 DATA "and tablets covered in dense calculations and notes. Whatever has been happening in here,"
|
||||
3610 DATA "it has been done with extreme urgency. Exits lead aft and to starboard."
|
||||
3615 REM lower forward corridor
|
||||
3620 DATA "This is a featureless, utilitarian corridor. A stairwell leads upwards. There are also"
|
||||
3630 DATA "doors leading to port and forward.", "", "", ""
|
||||
3660 REM pod bay
|
||||
3670 DATA "You are in a large room, with a row of spacesuits hanging on the port wall. At the forward"
|
||||
3680 DATA "end of the room is a small, two seater vehicle, capable of operating in space outside the"
|
||||
3690 DATA "main ship for limited periods. In front of the small ship is the pod bay door, leading out"
|
||||
3700 DATA "into space. There is a porthole on the far wall. There is a single exit leading aft.", ""
|
||||
3710 REM aft main corridor
|
||||
3720 DATA "The main corridor stretches away from you towards the front of the ship. It is featureless"
|
||||
3730 DATA "and utilitarian. The lighting is dim. You can see doors either side of you, to port and"
|
||||
3740 DATA "to starboard.", "", ""
|
||||
3760 REM mid main corridor
|
||||
3770 DATA "The main corridor stretches away from you both forward and aft. It is featureless"
|
||||
3780 DATA "and utilitarian. The lighting is dim. You can see doors either side of you, to port and"
|
||||
3790 DATA "to starboard.", "", ""
|
||||
3810 REM forward main corridor
|
||||
3820 DATA "The main corridor stretches away from you towards the rear of the ship. It is featureless"
|
||||
3830 DATA "and utilitarian. The lighting is dim. You can see doors either side of you, to port and"
|
||||
3840 DATA "to starboard, as well as a third door leading forward.", "", ""
|
||||
3860 REM assign to room descriptions
|
||||
3870 FOR ROOM = 0 to RC-1
|
||||
3880 FOR I = 0 TO 4
|
||||
3885 READ DESC$ : RD$ (ROOM, I) = DESC$
|
||||
3890 NEXT I
|
||||
3900 NEXT ROOM
|
||||
4000 RETURN
|
||||
4010 REM ========== print room description ==========
|
||||
4020 FOR LINE = 0 TO 4
|
||||
4030 IF RD$(PL, LINE) <> "" THEN PRINT RD$(PL, LINE)
|
||||
4040 NEXT LINE
|
||||
4055 GOSUB 4070
|
||||
4060 RETURN
|
||||
4070 REM print objects
|
||||
4080 FOR I = 0 TO OC-1
|
||||
4090 IF OL ( I ) = PL THEN PRINT : PRINT "You can see: ";OB$ ( I ); "."
|
||||
4100 NEXT I
|
||||
4110 PRINT
|
||||
4115 IF PL = LAB THEN GOSUB 9700
|
||||
4120 RETURN
|
||||
4130 REM ========== print help ==========
|
||||
4140 PRINT "For movement, try [go] a[ft], f[orward], p[ort], s[tarboard], u[p] or d[own]."
|
||||
4150 PRINT "For actions, try get, take, drop, examine, look, use, i[nventory], q[uit]."
|
||||
4155 PRINT "To examine, pick up, use or drop items, refer to them exactly as they are described."
|
||||
4157 PRINT
|
||||
4160 RETURN
|
||||
5000 REM ========== interactive item descriptions ==========
|
||||
5010 DIM ID$(IC, 20)
|
||||
5020 REM medical log
|
||||
5030 DATA "The last few entries of the medical log are still visible on the screen:", " "
|
||||
5040 DATA "'2142-6-13: Three days since chimpanzee Nova was innoculated with agent #53."
|
||||
5050 DATA " However, subject Nova showing no signs of adaptation to planetary destination"
|
||||
5060 DATA " atmosphere. In fact, she appears listless, though punctuated with periods of"
|
||||
5070 DATA " extreme agression.", " "
|
||||
5090 DATA " 2142-6-14: This morning, Dr Pearson was bitten by Nova. Very quickly he exhibited"
|
||||
5100 DATA " signs of a high fever and is now resting in his quarters.", " "
|
||||
5120 DATA " 2142-6-15: After a brief period of catatonia, Pearson got up on his own, returned"
|
||||
5130 DATA " to the medical bay and began to compulsively prepare more agent #53 samples. He"
|
||||
5140 DATA " is otherwise uncommunicative.", " "
|
||||
5160 DATA " 2142-6-17: Pearson's compulsive behaviour continues unabated. We have a hypothesis"
|
||||
5170 DATA " for what the virus, which we have now designated HO-1, does to the brain. However,"
|
||||
5180 DATA " we are struggling to isolate Pearson and may have to seal the medical bay.", " "
|
||||
5200 DATA " 2142-6-17: Hollow (HO-1) confirmed as airborne, and other cases appearing around the ship."
|
||||
5210 DATA " Shipwide lockdown declared but crew cohesion and discipline already breaking down.'"
|
||||
5215 REM porthole
|
||||
5220 DATA "Looking through the porthole, you seen a spacesuit clad figure floating outside."
|
||||
5230 DATA "Although he is tethered to an anchor point on the ship's hull, he is otherwise floating"
|
||||
5240 DATA "freely, his arms and legs splayed out to his sides.", " "
|
||||
5260 DATA "Peering at the figure more closely, as he slowly rotates, a light from the ship"
|
||||
5270 DATA "briefly illuminates his face. He's clearly dead, his oxygen ran out some time ago."
|
||||
5280 DATA "His expression is frozen in a rictus of pain. He was screaming almost until the end ..."
|
||||
5290 DATA "", "", "", "", "", "", "", "", "", "", "", "", ""
|
||||
5300 REM console
|
||||
5310 DATA "The console shows the state of the ship's engines. The pilot"
|
||||
5320 DATA "appears to have the throttle jammed wide open with his right arm. The muscles in his"
|
||||
5340 DATA "forearm are taught, there's no way he's going to release the throttle. He left hand"
|
||||
5350 DATA "works its way around the console switches. After a few moments you realise that he is"
|
||||
5360 DATA "executing the same sequence of switches over and over again.", " "
|
||||
5370 DATA "His eyes are glazed, mentally he is somewhere else. He is"
|
||||
5380 DATA "mumbling to himself but you cannot make out the words, although he appears to be"
|
||||
5390 DATA "reciting some sort of launch checklist.", " "
|
||||
5392 DATA "One screen shows the mission parameters:"
|
||||
5394 DATA " 1. Explore potentially habitable worlds"
|
||||
5396 DATA " 2. Conduct research to develop gene therapies to aid adaptation to planetary conditions"
|
||||
5398 DATA " 3. Conduct research to develop custom biologic weapons systems, to protect colonists", " "
|
||||
5400 DATA "If the engines are online, the ship will be careering through space at maximum"
|
||||
5410 DATA "speed. Rescue will be impossible unless the engines are offline."
|
||||
5420 DATA "", "", ""
|
||||
5430 REM engine control
|
||||
5440 DATA "The control panel is grubby, smeared with oil and grime. This is clearly"
|
||||
5450 DATA "the engineering heart of the ship. Most of the readouts mean nothing to you,"
|
||||
5460 DATA "whatever your duties were on this ship, you were clearly not a warp engineer.", " "
|
||||
5470 DATA "Many of the lights on the front panel are flashing red. Not all is well with"
|
||||
5480 DATA "the Pneuma. Even to your untrained eye, it's obvious that the ship's engines appear to"
|
||||
5490 DATA "be on the point of burnout, having been run at full capacity for many hours.", " "
|
||||
5500 DATA "To the top left of the panel, a screen reads:", " "
|
||||
5510 DATA "'WARNING: CORE BREACH IMMINENT'", " "
|
||||
5520 DATA "Just below the screen is a large red button, shielded by a cover that can be"
|
||||
5530 DATA "flipped aside.", "", "", "", "", "", ""
|
||||
5540 REM library terminal
|
||||
5550 DATA "A ancient text is open in the terminal: 'The Origin of Consciousness in the Breakdown"
|
||||
5560 DATA "of the Bicameral Mind by Julian Jaynes, 1976.'", "", "", "", "", "", "", "", "", ""
|
||||
5570 DATA "", "", "", "", "", "", "", "", ""
|
||||
5940 FOR OBJECT = 0 TO IC-1
|
||||
5950 FOR I = 0 TO 19
|
||||
5960 READ DESC$ : ID$ (OBJECT, I) = DESC$
|
||||
5970 NEXT I
|
||||
5980 NEXT OBJECT
|
||||
5990 RETURN
|
||||
6000 REM ========== print interative object description ==========
|
||||
6010 FOR LINE = 0 TO 19
|
||||
6020 IF ID$(F, LINE) <> "" THEN PRINT ID$(F, LINE)
|
||||
6030 NEXT LINE
|
||||
6035 PRINT
|
||||
6040 RETURN
|
||||
7000 REM ========== character speech ==========
|
||||
7010 REM chef
|
||||
7015 DIM PD$(PC, 4)
|
||||
7020 DATA "Carrots, potatoes, I'm going to make a nice beef stew."
|
||||
7030 DATA "This stew is going to be delicious."
|
||||
7060 REM runner
|
||||
7070 DATA "24:50, that's my new personal best. Not much further to go!"
|
||||
7080 DATA "24:50, that's my new personal best. Not much further to go!"
|
||||
7110 REM pilot
|
||||
7120 DATA "Main engines online, supercruise on my mark."
|
||||
7130 DATA "Five ... four ... three ... two ... one ... mark!"
|
||||
7140 FOR PERSON = 0 TO PC-1
|
||||
7150 FOR I = 0 TO 1
|
||||
7160 READ DESC$ : PD$ (PERSON, I) = DESC$
|
||||
7170 NEXT I
|
||||
7180 PD$(PERSON, 2) = PD$(PERSON, 0)
|
||||
7190 PD$(PERSON, 3) = PD$(PERSON, 1)
|
||||
7200 NEXT PERSON
|
||||
7210 RETURN
|
||||
7500 REM ========== print character speech ==========
|
||||
7510 FOR LINE = 0 TO 4
|
||||
7520 PRINT PD$(F, LINE)
|
||||
7530 NEXT LINE
|
||||
7535 PRINT
|
||||
7550 RETURN
|
||||
8000 REM ========== wraith-hound tracking ==========
|
||||
8010 IF OL ( TRACKER ) <> INV THEN RETURN
|
||||
8015 IF WPL = 99 THEN PRINT "Tracking: Wraith-hound terminated." : GOTO 8030
|
||||
8020 PRINT "Tracking: Wraith-hound current location is the "; LO$ ( WPL )
|
||||
8030 PRINT
|
||||
8040 RETURN
|
||||
8200 REM ========== wraith-hound proximity check ==========
|
||||
8210 DIR$ = ""
|
||||
8220 IF VAL ( MID$ ( EX$ ( PL ) , 1 , 2 ) ) = WPL THEN DIR$ = "forward of you"
|
||||
8230 IF VAL ( MID$ ( EX$ ( PL ) , 3 , 2 ) ) = WPL THEN DIR$ = "aft of you"
|
||||
8240 IF VAL ( MID$ ( EX$ ( PL ) , 5 , 2 ) ) = WPL THEN DIR$ = "to port"
|
||||
8250 IF VAL ( MID$ ( EX$ ( PL ) , 7 , 2 ) ) = WPL THEN DIR$ = "to starboard"
|
||||
8260 IF VAL ( MID$ ( EX$ ( PL ) , 9 , 2 ) ) = WPL THEN DIR$ = "above you"
|
||||
8270 IF VAL ( MID$ ( EX$ ( PL ) , 11 , 2 ) ) = WPL THEN DIR$ = "below you"
|
||||
8280 IF DIR$ <> "" THEN PRINT "You hear a snarling, spitting noise "; DIR$; ". The tracker is pinging ..."
|
||||
8285 PRINT
|
||||
8290 RETURN
|
||||
8400 REM ========== wraith-hound movement ==========
|
||||
8410 REM decide to move one third of the time
|
||||
8420 MOVE = RNDINT(1, 3)
|
||||
8430 IF MOVE <> 1 THEN RETURN
|
||||
8440 REM randomly determine direction
|
||||
8450 DIR = RNDINT(1, 6)
|
||||
8460 IF DIR = 1 THEN INDEX = 1
|
||||
8470 IF DIR = 2 THEN INDEX = 3
|
||||
8480 IF DIR = 3 THEN INDEX = 5
|
||||
8490 IF DIR = 4 THEN INDEX = 7
|
||||
8500 IF DIR = 5 THEN INDEX = 9
|
||||
8510 IF DIR = 6 THEN INDEX = 11
|
||||
8520 NWPL = VAL ( MID$ ( EX$ ( WPL ) , INDEX , 2 ) ) : REM potential next location
|
||||
8530 IF NWPL = 0 THEN GOTO 8450 : REM can't go that way
|
||||
8540 WPL = NWPL
|
||||
8550 RETURN
|
||||
9000 REM ========== use an item ==========
|
||||
9010 F=-1: R$=""
|
||||
9020 R$ = MID$(LOWER$(I$), 5) : REM R$ is the requested object
|
||||
9025 IF R$ <> "red button" THEN GOTO 9030
|
||||
9027 GOSUB 9500
|
||||
9028 RETURN
|
||||
9030 REM get the object ID
|
||||
9040 FOR I= 0 TO OC-1
|
||||
9050 IF OB$(I) = R$ THEN F=I : REM object exists
|
||||
9060 NEXT I
|
||||
9070 REM can't find the item?
|
||||
9080 IF F=-1 THEN PRINT "You haven't seen that item." : PRINT : RETURN
|
||||
9090 IF OL(F) <> 0 THEN PRINT "You haven't picked up that item." : PRINT : RETURN
|
||||
9100 REM item exists and is in inventory
|
||||
9110 IF F <> SYRINGE THEN GOTO 9140
|
||||
9112 IF OB$( SYRINGE ) = "blood filled syringe" THEN PRINT "The syringe is full." : PRINT : RETURN
|
||||
9115 IF SUIT_WORN = 1 THEN PRINT "You can't puncture a spacesuit with a syringe!" : PRINT : RETURN
|
||||
9120 OB$( SYRINGE ) = "blood filled syringe"
|
||||
9130 PRINT "You use the syringe to take a blood sample from your arm." : PRINT : RETURN
|
||||
9140 IF F <> TRACKER THEN GOTO 9160
|
||||
9150 PRINT "The tracker is always on." : PRINT
|
||||
9160 IF F <> SUIT THEN GOTO 9200
|
||||
9170 IF OL(F) <> 0 THEN PRINT "You don't have a space suit." : PRINT : RETURN
|
||||
9175 IF SUIT_WORN = 1 THEN PRINT "You're already wearing the space suit." : PRINT : RETURN
|
||||
9180 SUIT_WORN = 1
|
||||
9190 PRINT "You put on the space suit." : PRINT
|
||||
9400 RETURN
|
||||
9500 REM special button routine
|
||||
9510 IF PL <> ENG THEN PRINT "There is no red button here." : PRINT : RETURN
|
||||
9520 IF SHUTDOWN = 0 THEN GOTO 9540
|
||||
9525 SHUTDOWN = 0
|
||||
9530 PRINT "Engine restart initiated ... engines online." : PRINT
|
||||
9535 ID$ (ENGINE, 10) = "'WARNING: CORE BREACH IMMINENT'" : RETURN
|
||||
9540 SHUTDOWN = 1
|
||||
9550 PRINT "Engine shutdown initiated ... engines offline." : PRINT
|
||||
9555 ID$ (ENGINE, 10) = "'ENGINES OFFLINE'"
|
||||
9560 RETURN
|
||||
9700 REM ========== laboratory conversation ==========
|
||||
9710 PRINT "Dr Lascoe is stood in the laboratory. He is armed. He speaks to you." : PRINT
|
||||
9720 PRINT "'It's you! You were infected with HO-1, but you seem to be normal."
|
||||
9730 PRINT " Hollow destroys consciousness, it shuts down all self awareness in the brain."
|
||||
9740 PRINT " The patient still functions to a degree, but they're an automaton, endlessly"
|
||||
9750 PRINT " running the same mental subroutines like a piece of clockwork until the body"
|
||||
9760 PRINT " breaks down.'" : PRINT
|
||||
9770 PRINT "'I've been hiding in here to avoid contagion. Someone released the wraith-hound"
|
||||
9780 PRINT " from containment, but I've been able to keep it away with this weapon."
|
||||
9790 PRINT " But if you're immune, I can develop a vaccine from your blood.'" : PRINT
|
||||
9800 IF OL(SYRINGE) = 0 AND OB$(SYRINGE) = "blood filled syringe" THEN GOTO 9820
|
||||
9810 PRINT "'You need to get me a sample of your blood.'" : PRINT : RETURN
|
||||
9820 PRINT "'Ah, I see you have a blood sample, excellent.'" : PRINT
|
||||
9830 IF SUIT_WORN = 1 THEN GOTO 9855
|
||||
9840 PRINT " I still don't want to take the risk that you are contagious. Put on a space"
|
||||
9850 PRINT " suit and then I'll let you approach me.'" : PRINT: RETURN
|
||||
9855 PRINT "'Good, you're wearing a suit to avoid contaminating me.'" : PRINT
|
||||
9860 IF SHUTDOWN = 1 THEN GOTO 9885
|
||||
9870 PRINT "'Rescue will be impossible while the engines are running out of control."
|
||||
9880 PRINT " You'll need to take them offline.'": PRINT : RETURN
|
||||
9885 PRINT "'Good job on taking the engines offline, you've saved us both!"
|
||||
9890 PRINT " I'll work up the serum and we can wait for rescue.'" : PRINT
|
||||
9900 GOSUB 10000
|
||||
9910 RETURN
|
||||
10000 REM ========== finale ==========
|
||||
10010 PRINT "Congratulations, you have save Pneuma and helped Dr Lascoe develop"
|
||||
10020 PRINT "vaccine against HO-1, the Hollow virus."
|
||||
10030 PRINT : PRINT " THE END" : PRINT
|
||||
10040 STOP
|
||||
11000 REM ========== fight ==========
|
||||
11010 PRINT "The wraith-hound arrives. It is an abomination! A weapon system that"
|
||||
11020 PRINT "nature would never have created unaided. Everything about it is just"
|
||||
11030 PRINT "plain wrong on so many levels. One thing is clear, it is designed"
|
||||
11035 PRINT "purely for hunting and killing." : PRINT
|
||||
11040 INPUT "Do you r[un] or f[ight]?"; I$
|
||||
11050 PRINT
|
||||
11060 IF LOWER$ ( I$ ) = "f" OR LOWER$ ( I$ ) = "fight" THEN GOTO 11190
|
||||
11065 IF LOWER$ ( I$ ) = "r" OR LOWER$ ( I$ ) = "run" THEN GOTO 11070
|
||||
11067 PRINT "Command unrecognised, you must fight!" : PRINT : GOTO 11190
|
||||
11070 REM run away to a random adjoining room
|
||||
11080 DIR = RNDINT(1, 6)
|
||||
11090 IF DIR = 1 THEN INDEX = 1
|
||||
11100 IF DIR = 2 THEN INDEX = 3
|
||||
11110 IF DIR = 3 THEN INDEX = 5
|
||||
11120 IF DIR = 4 THEN INDEX = 7
|
||||
11130 IF DIR = 5 THEN INDEX = 9
|
||||
11140 IF DIR = 6 THEN INDEX = 11
|
||||
11150 NPL = VAL ( MID$ ( EX$ ( PL ) , INDEX , 2 ) ) : REM potential next location
|
||||
11160 IF NPL = 0 THEN GOTO 11080 : REM can't go that way
|
||||
11170 PL = NPL : PRINT "You are in the " ; LO$ ( PL ) : PRINT
|
||||
11175 GOSUB 4010
|
||||
11180 RETURN
|
||||
11190 REM fight
|
||||
11200 FEROCITY = 60 : REM ferocity factor for wraith-hound
|
||||
11205 STAMINA = 50 : REM your stamina
|
||||
11210 IF OL(PULSE) <> INV AND OL(KNIFE) <> INV THEN PRINT "You have no weapons, and must fight bare handed." : PRINT : GOTO 11280
|
||||
11220 IF OL(PULSE) = INV AND OL(KNIFE) <> INV THEN PRINT "Luckily, you have a pulse rifle." : PRINT: FEROCITY=50 : GOTO 11280
|
||||
11230 IF OL(PULSE) <> INV AND OL(KNIFE) = INV THEN PRINT "You only have a knife with which to fight." : PRINT : FEROCITY = 55 : GOTO 11280
|
||||
11240 INPUT "Which weapon do you choose? 1 - Pulse rifle, 2 - Knife: "; WPN
|
||||
11245 PRINT
|
||||
11250 IF WPN<1 OR WPN>2 THEN GOTO 11240
|
||||
11260 IF WPN=1 THEN FEROCITY = 50
|
||||
11270 IF WPN=2 THEN FEROCITY = 55
|
||||
11280 REM the battle
|
||||
11290 IF RND(1)>0.5 THEN PRINT "The wraith-hound attacks!" ELSE PRINT "You attack!"
|
||||
11300 PRINT
|
||||
11305 GOSUB 11430
|
||||
11310 IF RND(1)>0.5 THEN PRINT "You wound the creature." : PRINT: FEROCITY = FEROCITY-5 : GOTO 11330
|
||||
11320 PRINT "The wraith-hound wounds you." : PRINT : STAMINA = STAMINA-5
|
||||
11330 GOSUB 11430
|
||||
11340 IF FEROCITY > 0 THEN GOTO 11390
|
||||
11350 PRINT "You slayed the wraith-hound!" : PRINT
|
||||
11360 REM remove creature from the game
|
||||
11370 WPL = 99 : REM inaccessible location
|
||||
11380 RETURN
|
||||
11390 IF STAMINA > 0 THEN GOTO 11420
|
||||
11400 PRINT "You were slayed by the wraith-hound! You have failed to save the Pneuma." : PRINT
|
||||
11410 STOP
|
||||
11420 GOTO 11280 : REM next round of the battle
|
||||
11430 REM ========== delay loop ==========
|
||||
11440 FOR T = 1 TO 80000
|
||||
11450 NEXT T
|
||||
11460 RETURN
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
682
builtin_apps/PyBasic/examples/PyBStartrek.bas
Normal file
|
|
@ -0,0 +1,682 @@
|
|||
520 REM
|
||||
530 REM
|
||||
540 REM **** **** STAR TREK **** ****
|
||||
550 REM **** Simulation of a mission of the starship ENTERPRISE
|
||||
560 REM **** as seen on the Star Trek tv show
|
||||
570 REM **** Original program in Creative Computing
|
||||
580 REM **** Basic Computer Games by Dave Ahl
|
||||
590 REM **** Modifications by Bob Fritz and Sharon Fritz
|
||||
600 REM **** for the IBM Personal Computer, October-November 1981
|
||||
610 REM **** Bob Fritz, 9915 Caninito Cuadro, San Diego, Ca., 92129
|
||||
620 REM **** (714) 484-2955
|
||||
630 REM ****
|
||||
640 PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT
|
||||
650 E1$= " ,-----------------, _"
|
||||
660 E2$= " `---------- ----',-----/ \----,"
|
||||
670 E3$= " | | '- --------'"
|
||||
680 E4$= " ,--' '------/ /--,"
|
||||
690 E5$= " '-----------------'"
|
||||
691 PRINT
|
||||
700 E6$= " THE USS ENTERPRISE --- NCC-1701"
|
||||
720 PRINT E1$
|
||||
730 PRINT E2$
|
||||
740 PRINT E3$
|
||||
750 PRINT E4$
|
||||
760 PRINT E5$
|
||||
770 PRINT
|
||||
780 PRINT E6$
|
||||
790 PRINT:PRINT: PRINT:PRINT:PRINT:PRINT:PRINT
|
||||
811 RANDOMIZE
|
||||
960 dim D(8)
|
||||
961 dim C(9,2)
|
||||
962 dim K(3,3)
|
||||
963 dim N(3)
|
||||
965 DIM G(8,8)
|
||||
966 DIM Z(8,8)
|
||||
970 T=INT(RND(1)*20+20)*100:T0=T:T9=25+INT(RND(1)*10):D0=0:E=3000:E0=E
|
||||
980 P=10:P0=P:S9=200:S=0:B9=0:K9=0:X$="":X0$=" is "
|
||||
990 rem DEF FND(D)=SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2)
|
||||
1000 rem DEF FNR(R)=INT(RND(1)(R)*7.98+1.01)
|
||||
1010 REM initialize enterprise's position
|
||||
1020 Q1=INT(RND(1)*7.98+1.01):Q2=INT(RND(1)*7.98+1.01):S1=INT(RND(1)*7.98+1.01):S2=INT(RND(1)*7.98+1.01)
|
||||
1030 FOR I=1 TO 9
|
||||
1031 C(I,1)=0:C(I,2)=0
|
||||
1032 NEXT I
|
||||
1040 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1
|
||||
1050 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1
|
||||
1060 FOR I=1 TO 8
|
||||
1061 D(I)=0
|
||||
1062 NEXT I
|
||||
1070 A1$="NAVSRSLRSPHATORSHIDAMCOMRES"
|
||||
1080 REM set up what exists in galaxy
|
||||
1090 REM k3=#klingons b3=#starbases s3=#stars
|
||||
1100 FOR I=1 TO 8
|
||||
1101 FOR J=1 TO 8
|
||||
1102 K3=0:Z(I,J)=0:R1=RND(1)
|
||||
1110 IF R1<=0.9799999 THEN goto 1120
|
||||
1111 K3=3:K9=K9+3: GOTO 1140
|
||||
1120 IF R1<=0.95 THEN goto 1130
|
||||
1121 K3=2:K9=K9+2: GOTO 1140
|
||||
1130 IF R1<=0.8 THEN goto 1140
|
||||
1131 K3=1:K9=K9+1
|
||||
1140 B3=0:IF RND(1)<=0.96 THEN goto 1150
|
||||
1141 B3=1:B9=B9+1
|
||||
1150 G(I,J)=K3*100+B3*10+INT(RND(1)*7.98+1.01)
|
||||
1151 NEXT J
|
||||
1152 NEXT I
|
||||
1153 IF K9<=T9 THEN goto 1160
|
||||
1154 T9=K9+1
|
||||
1160 IF B9<>0 THEN 1190
|
||||
1170 IF G(Q1,Q2)>=200 THEN 1180
|
||||
1171 G(Q1,Q2)=G(Q1,Q2)+100:K9=K9+1
|
||||
1180 B9=1:G(Q1,Q2)=G(Q1,Q2)+10:Q1=INT(RND(1)*7.98+1.01):Q2=INT(RND(1)*7.98+1.01)
|
||||
1190 K7=K9:IF B9=1 THEN 1200
|
||||
1191 X$="s":X0$=" are "
|
||||
1200 PRINT" Your orders are as follows: "
|
||||
1210 PRINT" Destroy the ";K9;" Klingon warships which have invaded"
|
||||
1220 PRINT" the galaxy before they can attack Federation headquarters"
|
||||
1230 PRINT" on stardate ";T0+T9;". This gives you ";T9;" days. there";X0$
|
||||
1240 PRINT" ";B9;" starbase";X$;" in the galaxy for resupplying your ship"
|
||||
1261 PRINT:INPUT"hit return when ready to accept command";i$
|
||||
1270 REM here any time new quadrant entered
|
||||
1280 Z4=Q1:Z5=Q2:K3=0:B3=0:S3=0:G5=0:D4=0.5*RND(1):Z(Q1,Q2)=G(Q1,Q2)
|
||||
1290 IF Q1<1 OR Q1>8 OR Q2<1 OR Q2>8 THEN 1410
|
||||
1300 GOSUB 5040
|
||||
1301 PRINT:IF T0 <>T THEN 1330
|
||||
1310 PRINT"Your mission begins with your starship located"
|
||||
1320 PRINT"in the galactic quadrant, '";G2$;"'.":GOTO 1340
|
||||
1330 PRINT"Now entering ";G2$;" quadrant. . ."
|
||||
1340 PRINT:K3=INT(G(Q1,Q2)*0.01):B3=INT(G(Q1,Q2)*0.1)-10*K3
|
||||
1350 S3=G(Q1,Q2)-100*K3-10*B3:IF K3=0 THEN 1400
|
||||
1360 PRINT "COMBAT AREA!! Condition";
|
||||
1370 PRINT " RED "; : PRINT
|
||||
1380 GOSUB 5290
|
||||
1381 IF S>200 THEN 1400
|
||||
1390 PRINT" SHIELDS DANGEROUSLY LOW"; : PRINT
|
||||
1400 FOR I=1 TO 3
|
||||
1401 K(I,1)=0:K(I,2)=0
|
||||
1402 NEXT I
|
||||
1410 FOR I=1 TO 3
|
||||
1411 K(I,3)=0
|
||||
1412 NEXT I
|
||||
1413 Q$=" "*192
|
||||
1420 REM position enterprise in quadrant, then place "k3" klingons,&
|
||||
1430 REM "b3" starbases & "s3" stars elsewhere.
|
||||
1440 A$="\e/":Z1=S1:Z2=S2
|
||||
1441 GOSUB 4830
|
||||
1442 IF K3<1 THEN 1470
|
||||
1450 FOR I=1 TO K3
|
||||
1451 GOSUB 4800
|
||||
1452 A$=chr$(187)+"K"+chr$(171):Z1=R1:Z2=R2
|
||||
1460 GOSUB 4830
|
||||
1461 K(I,1)=R1:K(I,2)=R2:K(I,3)=S9*(0.5+RND(1))
|
||||
1462 NEXT I
|
||||
1470 IF B3<1 THEN 1500
|
||||
1480 GOSUB 4800
|
||||
1481 A$="("+chr$(174)+")":Z1=R1:B4=R1:Z2=R2:B5=R2
|
||||
1490 GOSUB 4830
|
||||
1500 FOR I=1 TO S3
|
||||
1501 GOSUB 4800
|
||||
1502 A$=" * ":Z1=R1:Z2=R2
|
||||
1503 GOSUB 4830
|
||||
1504 NEXT I
|
||||
1510 GOSUB 3720
|
||||
1520 IF S+E<=10 THEN 1530
|
||||
1521 IF E>10 OR D(7)>=0 THEN 1580
|
||||
1530 PRINT"*** FATAL ERROR ***";
|
||||
1531 GOSUB 5290
|
||||
1540 PRINT"You've just stranded your ship in "
|
||||
1550 PRINT"space":PRINT"You have insufficient maneuvering energy,";
|
||||
1560 PRINT" and shield control":PRINT"is presently incapable of cross";
|
||||
1570 PRINT"-circuiting to engine room!!":GOTO 3480
|
||||
1580 INPUT"command ";A$
|
||||
1590 FOR I=1 TO 9
|
||||
1591 IF MID$(A$,1,3)<> MID$(A1$,3*I-2,3) THEN 1610
|
||||
1600 ON I GOTO 1720,1510,2440,2530,2750,3090,3180,3980,3510
|
||||
1610 NEXT I
|
||||
1611 PRINT"Enter one of the following:"
|
||||
1620 PRINT" NAV (to set course)"
|
||||
1630 PRINT" SRS (for short range sensor scan)"
|
||||
1640 PRINT" LRS (for long range sensor scan)"
|
||||
1650 PRINT" PHA (to fire phasers)"
|
||||
1660 PRINT" TOR (to fire photon torpedoes)"
|
||||
1670 PRINT" SHI (to raise or lower shields)"
|
||||
1680 PRINT" DAM (for damage control reports)"
|
||||
1690 PRINT" COM (to call on library-computer)"
|
||||
1700 PRINT" RES (to resign your command)":PRINT:GOTO 1520
|
||||
1710 REM course control begins here
|
||||
1720 INPUT"Course (1-9) ";C2$:C1=VAL(C2$):IF C1<>9 THEN 1730
|
||||
1721 C1=1
|
||||
1730 IF C1>=1 AND C1<9 THEN 1750
|
||||
1740 PRINT" Lt. Sulu reports, 'Incorrect course data, sir!'":GOTO 1520
|
||||
1750 X$="8":IF D(1)>=0 THEN 1760
|
||||
1751 X$="0.2"
|
||||
1760 PRINT"Warp factor(0-";X$;") ";:INPUT C2$:W1=VAL(C2$):IF D(1)<0 AND W1>0.2 THEN 1810
|
||||
1770 IF W1>0 AND W1<8 THEN 1820
|
||||
1780 IF W1=0 THEN 1520
|
||||
1790 PRINT" Chief Engineer Scott reports 'The engines won't take";
|
||||
1800 PRINT" warp ";W1;"!":GOTO 1520
|
||||
1810 PRINT"Warp engines are damaged. Maximum speed = warp 0.2":GOTO 1520
|
||||
1820 N1=INT(W1*8+0.5):IF E-N1>=0 THEN 1900
|
||||
1830 PRINT"Engineering reports 'Insufficient energy available"
|
||||
1840 PRINT" for maneuvering at warp ";W1;"!'"
|
||||
1850 IF S<N1-E OR D(7)<0 THEN 1520
|
||||
1860 PRINT"Deflector control room acknowledges ";S;" units of energy"
|
||||
1870 PRINT" presently deployed to shields."
|
||||
1880 GOTO 1520
|
||||
1890 REM klingons move/fire on moving starship . . .
|
||||
1900 FOR I=1 TO K3
|
||||
1901 IF K(I,3)=0 THEN 1930
|
||||
1910 A$=" ":Z1=K(I,1):Z2=K(I,2)
|
||||
1911 GOSUB 4830
|
||||
1912 GOSUB 4800
|
||||
1920 K(I,1)=Z1:K(I,2)=Z2:A$=chr$(187)+"K"+chr$(171)
|
||||
1921 GOSUB 4830
|
||||
1930 NEXT I
|
||||
1931 REM GOSUB 4810
|
||||
1932 D1=0:D6=W1:IF W1<1 THEN 1940
|
||||
1933 D6=1
|
||||
1940 FOR I=1 TO 8
|
||||
1941 IF D(I)>=0 THEN 1990
|
||||
1950 D(I)=min(0,D(I)+D6):IF D(I)<=-0.1 or D(I)>=0 THEN 1960
|
||||
1951 D(I)=-0.1
|
||||
1952 GOTO 1990
|
||||
1960 IF D(I)<0 THEN 1990
|
||||
1970 IF D1=1 THEN 1980
|
||||
1971 D1=1:PRINT"DAMAGE CONTROL REPORT: ";
|
||||
1980 PRINT TAB(8);:R1=I
|
||||
1981 GOSUB 4890
|
||||
1982 PRINT G2$;" Repair completed."
|
||||
1990 NEXT I
|
||||
1991 IF RND(1)>0.2 THEN 2070
|
||||
2000 R1=INT(RND(1)*7.98+1.01):IF RND(1)>=0.6 THEN 2040
|
||||
2010 IF K3=0 THEN 2070
|
||||
2020 D(R1)=D(R1)-(RND(1)*5+1):PRINT"DAMAGE CONTROL REPORT: ";
|
||||
2030 GOSUB 4890
|
||||
2031 PRINT G2$;" damaged":PRINT:GOTO 2070
|
||||
2040 D(R1)=min(0,D(R1)+RND(1)*3+1):PRINT"DAMAGE CONTROL REPORT: ";
|
||||
2050 GOSUB 4890
|
||||
2051 PRINT G2$;" State of repair improved":PRINT
|
||||
2060 REM begin moving starship
|
||||
2070 A$=" " :Z1=INT(S1):Z2=INT(S2)
|
||||
2071 GOSUB 4830
|
||||
2079 Z1=INT(C1):C1=C1-Z1
|
||||
2080 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1:X=S1:Y=S2
|
||||
2090 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1:Q4=Q1:Q5=Q2
|
||||
2100 FOR I=1 TO N1
|
||||
2101 S1=S1+X1:S2=S2+X2:IF S1<1 OR S1>=9 OR S2<1 OR S2>=9 THEN 2220
|
||||
2110 S8=INT(S1)*24+INT(S2)*3-26:IF MID$(Q$,S8+1,2)=" " THEN 2140
|
||||
2120 S1=INT(S1-X1):S2=INT(S2-X2):PRINT"Warp engines shut down at ";
|
||||
2130 PRINT "sector ";S1;",";S2;" due to bad navigation.":GOTO 2150
|
||||
2140 NEXT I
|
||||
2141 S1=INT(S1):S2=INT(S2)
|
||||
2150 A$="\e/"
|
||||
2160 Z1=INT(S1):Z2=INT(S2)
|
||||
2161 GOSUB 4830
|
||||
2162 GOSUB 2390
|
||||
2163 T8=1
|
||||
2170 IF W1>=1 THEN 2180
|
||||
2171 T8=0.1*INT(10*W1)
|
||||
2180 T=T+T8:IF T>T0+T9 THEN 3480
|
||||
2190 REM see if docked then get command
|
||||
2200 GOTO 1510
|
||||
2210 REM exceeded quadrant limits
|
||||
2220 X=8*Q1+X+N1*X1:Y=8*Q2+Y+N1*X2:Q1=INT(X/8):Q2=INT(Y/8):S1=INT(X-Q1*8)
|
||||
2230 S2=INT(Y-Q2*8):IF S1<>0 THEN 2240
|
||||
2231 Q1=Q1-1:S1=8
|
||||
2240 IF S2<>0 THEN 2250
|
||||
2241 Q2=Q2-1:S2=8
|
||||
2250 X5=0:IF Q1>=1 THEN 2260
|
||||
2251 X5=1:Q1=1:S1=1
|
||||
2260 IF Q1<=8 THEN 2270
|
||||
2261 X5=1:Q1=8:S1=8
|
||||
2270 IF Q2>=1 THEN 2280
|
||||
2271 X5=1:Q2=1:S2=1
|
||||
2280 IF Q2<=8 THEN 2290
|
||||
2281 X5=1:Q2=8:S2=8
|
||||
2290 IF X5=0 THEN 2360
|
||||
2300 PRINT"Lt. Uhura reports message from Starfleet Command:"
|
||||
2310 PRINT" 'Permission to attempt crossing of galactic perimeter"
|
||||
2320 PRINT" is hereby *DENIED*. Shut down your engines.'"
|
||||
2330 PRINT"Chief Engineer Scott reports 'Warp engines shut down"
|
||||
2340 PRINT" at sector ";S1;",";S2;" of quadrant ";Q1;",";Q2".'"
|
||||
2350 IF T>T0 THEN 3480
|
||||
2360 IF 8*Q1+Q2=8*Q4+Q5 THEN 2150
|
||||
2370 T=T+1
|
||||
2371 GOSUB 2390
|
||||
2372 GOTO 1280
|
||||
2380 REM maneuver energy s/r **
|
||||
2390 E=E-N1-10:IF E<=0 THEN 2400
|
||||
2391 RETURN
|
||||
2400 PRINT"Shield control supplies energy to complete the maneuver."
|
||||
2410 S=S+E:E=0:IF S<=0 THEN 2420
|
||||
2411 S=0
|
||||
2420 RETURN
|
||||
2430 REM long range sensor scan code
|
||||
2440 IF D(3)>=0 THEN 2450
|
||||
2441 PRINT"Long Range Sensors are inoperable":GOTO 1520
|
||||
2450 PRINT"Long Range Scan for quadrant ";Q1;",";Q2
|
||||
2460 PRINT "-"*19
|
||||
2470 FOR I=Q1-1 TO Q1+1
|
||||
2471 N(1)=-1:N(2)=-2:N(3)=-3
|
||||
2472 FOR J=Q2-1 TO Q2+1
|
||||
2480 IF I<=0 or I>=9 or J<=0 or J>=9 THEN 2490
|
||||
2481 N(J-Q2+2)=G(I,J)
|
||||
2482 REM added so long range sensor scans are added to computer database
|
||||
2483 z(i,j)=g(i,j)
|
||||
2490 NEXT J
|
||||
2491 FOR L=1 TO 3
|
||||
2492 PRINT"| ";
|
||||
2493 IF N(L)>=0 THEN 2500
|
||||
2494 PRINT"*** ";:GOTO 2510
|
||||
2500 PRINT MID$(STR$(N(L)+1000),2,3);" ";
|
||||
2510 NEXT L
|
||||
2511 PRINT"|"
|
||||
2512 PRINT "-"*19
|
||||
2513 NEXT I
|
||||
2514 GOTO 1520
|
||||
2520 REM phaser control code begins here
|
||||
2530 IF D(4)>=0 THEN 2540
|
||||
2531 PRINT"Phasers Inoperative":GOTO 1520
|
||||
2540 IF K3>0 THEN 2570
|
||||
2550 PRINT"Science Officer Spock reports 'Sensors show no enemy ships"
|
||||
2560 PRINT" in this quadrant'":GOTO 1520
|
||||
2570 IF D(8)>=0 THEN 2580
|
||||
2571 PRINT"Computer failure hampers accuracy"
|
||||
2580 PRINT"Phasers locked on target ";
|
||||
2590 PRINT"Energy available = ";E;" units"
|
||||
2600 INPUT"Numbers of units to fire ";X:IF X<=0 THEN 1520
|
||||
2610 IF E-X<0 THEN 2590
|
||||
2620 E=E-X
|
||||
2621 GOSUB 5420
|
||||
2622 IF D(7)<0 THEN 2630
|
||||
2623 X=X*RND(1)
|
||||
2630 H1=INT(X/K3)
|
||||
2631 FOR I=1 TO 3
|
||||
2632 IF K(I,3)<=0 THEN 2730
|
||||
2640 KSQ1 = (K(I,1)-S1)*(K(I,1)-S1)
|
||||
2641 KSQ2 = (K(I,2)-S2)*(K(I,2)-S2)
|
||||
2642 H= SQR( KSQ1 + KSQ2 )
|
||||
2646 H = H1 / H
|
||||
2647 H = INT(H * (RND(1)+2))
|
||||
2648 IF H>0.15*K(I,3) THEN 2660
|
||||
2650 PRINT"Sensors show no damage to enemy at ";K(I,1);",";K(I,2):GOTO 2730
|
||||
2660 K(I,3)=K(I,3)-H:PRINT H;"Unit hit on Klingon at sector ";K(I,1);",";
|
||||
2670 PRINT K(I,2):IF K(I,3)> 0 THEN GOTO 2700
|
||||
2680 PRINT "**** KLINGON DESTROYED ****"
|
||||
2690 GOTO 2710
|
||||
2700 PRINT" (Sensors show ";K(I,3);" units remaining)":GOTO 2730
|
||||
2710 K3=K3-1:K9=K9-1:Z1=K(I,1):Z2=K(I,2):A$=" "
|
||||
2711 GOSUB 4830
|
||||
2720 K(I,3)=0:G(Q1,Q2)=G(Q1,Q2)-100:Z(Q1,Q2)=G(Q1,Q2):IF K9<=0 THEN 3680
|
||||
2730 NEXT I
|
||||
2731 GOSUB 3350
|
||||
2732 GOTO 1520
|
||||
2740 REM photon torpedo code begins here
|
||||
2750 IF P>0 THEN 2760
|
||||
2751 PRINT"All photon torpedoes expended":GOTO 1520
|
||||
2760 IF D(5)>=0 THEN 2770
|
||||
2761 PRINT"Photon tubes are not operational":GOTO 1520
|
||||
2770 INPUT"Photon torpedo course (1-9) ";C2$:C1=VAL(C2$):IF C1<>9 THEN 2780
|
||||
2771 C1=1
|
||||
2780 IF C1>=1 AND C1<9 THEN 2810
|
||||
2790 PRINT"Ensign Chekov reports, 'Incorrect course data, sir!'"
|
||||
2800 GOTO 1520
|
||||
2810 Z1=INT(C1):C1=C1-Z1
|
||||
2811 X1=C(Z1,1)+(C(Z1+1,1)-C(Z1,1))*C1:E=E-2:P=P-1
|
||||
2820 X2=C(Z1,2)+(C(Z1+1,2)-C(Z1,2))*C1:X=S1:Y=S2
|
||||
2821 GOSUB 5360
|
||||
2830 PRINT"Torpedo track:"
|
||||
2840 X=X+X1:Y=Y+X2:X3=INT(X+0.5):Y3=INT(Y+0.5)
|
||||
2850 IF X3<1 OR X3>8 OR Y3<1 OR Y3>8 THEN 3070
|
||||
2860 PRINT" ";X3;",";Y3:A$=" ":Z1=X:Z2=Y
|
||||
2861 GOSUB 4990
|
||||
2870 IF Z3<>0 THEN 2840
|
||||
2880 A$=chr$(187)+"K"+chr$(171):Z1=X:Z2=Y
|
||||
2881 GOSUB 4990
|
||||
2882 IF Z3=0 THEN 2940
|
||||
2890 PRINT"**** KLINGON DESTROYED ****"
|
||||
2900 K3=K3-1:K9=K9-1:IF K9<=0 THEN 3680
|
||||
2910 FOR I=1 TO 3
|
||||
2911 IF X3=K(I,1) AND Y3=K(I,2) THEN 2930
|
||||
2920 NEXT I
|
||||
2921 I=3
|
||||
2930 K(I,3)=0:GOTO 3050
|
||||
2940 A$=" * ":Z1=X:Z2=Y
|
||||
2941 GOSUB 4990
|
||||
2942 IF Z3=0 THEN 2960
|
||||
2950 PRINT"Star at ";X3;",";Y3;" absorbed torpedo energy."
|
||||
2951 GOSUB 3350
|
||||
2952 GOTO 1520
|
||||
2960 A$="("+chr$(174)+")":Z1=X:Z2=Y
|
||||
2961 GOSUB 4990
|
||||
2962 IF Z3<>0 THEN 2970
|
||||
2963 PRINT "Torpedo absorbed by unknown object at ";x3;",";y3
|
||||
2964 goto 1520
|
||||
2970 PRINT"*** STARBASE DESTROYED ***"
|
||||
2980 B3=B3-1 : B9=B9-1
|
||||
2990 IF B9>0 OR K9>T-T0-T9 THEN 3030
|
||||
3000 PRINT"THAT DOES IT, CAPTAIN!! You are hereby relieved of command"
|
||||
3010 PRINT"and sentenced to 99 stardates of hard labor on CYGNUS 12!!"
|
||||
3020 GOTO 3510
|
||||
3030 PRINT"Starfleet reviewing your record to consider"
|
||||
3040 PRINT"court martial!":D0=0
|
||||
3050 Z1=X:Z2=Y:A$=" "
|
||||
3051 GOSUB 4830
|
||||
3060 G(Q1,Q2)=K3*100+B3*10+S3:Z(Q1,Q2)=G(Q1,Q2)
|
||||
3061 GOSUB 3350
|
||||
3062 GOTO 1520
|
||||
3070 PRINT"Torpedo missed"
|
||||
3071 GOSUB 3350
|
||||
3072 GOTO 1520
|
||||
3080 REM shield control
|
||||
3090 IF D(7)>=0 THEN 3100
|
||||
3091 PRINT"Shield control inoperable":GOTO 1520
|
||||
3100 PRINT"Energy available = ";E+S :INPUT "Number of units to shields? ";X
|
||||
3110 IF X>=0 and S<>X THEN 3120
|
||||
3111 PRINT"<shields unchanged>":GOTO 1520
|
||||
3120 IF X<E+S THEN 3150
|
||||
3130 PRINT"Shield Control reports: This is not the federation treasury."
|
||||
3140 PRINT"<shields unchanged>":goto 1990
|
||||
3150 E=E+S-X:S=X:PRINT"Deflector Control Room report:"
|
||||
3160 PRINT" 'Shields now at ";INT(S);" units per your command.'":GOTO 1520
|
||||
3170 REM damage control
|
||||
3180 IF D(6)>=0 THEN 3290
|
||||
3190 PRINT"Damage control report not available":IF D0=0 THEN 1520
|
||||
3200 D3=0:FOR I=1 TO 8
|
||||
3201 IF D(I)>=0 THEN 3210
|
||||
3202 D3=D3+1
|
||||
3210 NEXT I
|
||||
3211 IF D3=0 THEN 1520
|
||||
3220 PRINT:D3=D3+D4:IF D3<1 THEN 3230
|
||||
3221 D3=0.9
|
||||
3230 PRINT"Technicians standing by to effect repairs to your ship;"
|
||||
3240 PRINT"estimated time to repair: ";0.01*INT(100*D3);" stardates"
|
||||
3250 INPUT"Will you authorize the repair order (Y/N)? ";A$
|
||||
3260 IF A$<>"y" AND A$<> "Y" THEN 1520
|
||||
3270 FOR I=1 TO 8
|
||||
3271 IF D(I)>=0 THEN 3280
|
||||
3272 D(I)=0
|
||||
3280 NEXT I
|
||||
3281 T=T+D3+0.1
|
||||
3290 PRINT:PRINT"Device state of repair"
|
||||
3291 FOR R1=1 TO 8
|
||||
3300 GOSUB 4890
|
||||
3301 PRINT G2$;tab(25);
|
||||
3310 GG2=INT(D(R1)*100)*0.01:PRINT GG2
|
||||
3320 NEXT R1
|
||||
3321 PRINT:IF D0<>0 THEN 3200
|
||||
3330 GOTO 1520
|
||||
3340 REM klingons shooting
|
||||
3350 IF K3>0 THEN 3360
|
||||
3351 RETURN
|
||||
3360 IF D0=0 THEN 3370
|
||||
3361 PRINT"Starbase shields protect the ENTERPRISE"
|
||||
3362 RETURN
|
||||
3370 FOR I=1 TO 3
|
||||
3371 IF K(I,3)<=0 THEN 3460
|
||||
3380 ksq1 = (K(I,1)-S1)*(K(I,1)-S1)
|
||||
3381 ksq2 = (K(I,2)-S2)*(K(I,2)-S2)
|
||||
3382 H=( K(I,3) / SQR( ksq1 + ksq2 ) )*(2+RND(1))
|
||||
3383 h = int(h)
|
||||
3384 S=S-H:K(I,3)=K(I,3)/(3+RND(1))
|
||||
3390 PRINT "ENTERPRISE HIT!"
|
||||
3400 GOSUB 5480
|
||||
3401 PRINT H;" Unit hit on ENTERPRISE from sector ";K(I,1);",";K(I,2)
|
||||
3410 IF S<=0 THEN 3490
|
||||
3420 PRINT" <shields down to ";S;" units>":IF H<20 THEN 3460
|
||||
3430 IF RND(1)>0.6 OR H/S<=0.02 THEN 3460
|
||||
3440 R1=INT(RND(1)*7.98+1.01):D(R1)=D(R1)-H/S-0.5*RND(1)
|
||||
3441 GOSUB 4890
|
||||
3450 PRINT"Damage control reports '";G2$;" damaged by the hit'"
|
||||
3460 NEXT I
|
||||
3461 RETURN
|
||||
3470 REM end of game
|
||||
3480 PRINT"It is stardate";T:GOTO 3510
|
||||
3490 PRINT:PRINT"the ENTERPRISE has been destroyed. The Federation ";
|
||||
3500 PRINT"will be conquered":GOTO 3480
|
||||
3510 PRINT"There were ";K9;" Klingon battle cruisers left at"
|
||||
3520 PRINT"the end of your mission"
|
||||
3530 PRINT:PRINT:IF B9=0 THEN 3670
|
||||
3540 PRINT"The Federation is in need of a new starship commander"
|
||||
3550 PRINT"for a similar mission -- if there is a volunteer,"
|
||||
3560 INPUT"let him or her step forward and enter 'AYE' ";X$:IF X$="AYE" THEN 520
|
||||
3670 END
|
||||
3680 PRINT"Congratulations, Captain! the last Klingon battle cruiser"
|
||||
3690 PRINT"menacing the Federation has been destroyed.":PRINT
|
||||
3700 PRINT"Your efficiency rating is ";:cc1 = k7/(t-t0):PRINT 1000*cc1*cc1:GOTO 3530
|
||||
3710 REM short range sensor scan & startup subroutine
|
||||
3720 A$="("+chr$(174)+")":Z3=0
|
||||
3721 FOR I=S1-1 TO S1+1
|
||||
3722 FOR J=S2-1 TO S2+1
|
||||
3730 IF INT(I+0.5)<1 OR INT(I+0.5)>8 OR INT(J+0.5)<1 OR INT(J+0.5)>8 or Z3=1 THEN 3760
|
||||
3750 Z1=I:Z2=J
|
||||
3751 GOSUB 4990
|
||||
3760 NEXT J
|
||||
3761 NEXT I
|
||||
3762 IF Z3=1 THEN 3770
|
||||
3763 D0=0:GOTO 3790
|
||||
3770 D0=1:C$="docked":E=E0:P=P0
|
||||
3780 PRINT"Shields dropped for docking purposes":S=0:GOTO 3810
|
||||
3790 IF K3<=0 THEN 3800
|
||||
3791 C$="*red*":GOTO 3810
|
||||
3800 C$="GREEN":IF E>=E0*0.1 THEN 3810
|
||||
3801 C$="YELLOW"
|
||||
3810 IF D(2)>=0 THEN 3830
|
||||
3820 PRINT:PRINT"*** Short Range Sensors are out ***":PRINT
|
||||
3821 RETURN
|
||||
3830 PRINT "-"*33
|
||||
3832 FOR I=1 TO 8
|
||||
3840 FOR J=(I-1)*24 TO (I-1)*24+21 STEP 3
|
||||
3850 IF MID$(Q$,J+1,3)<>" " THEN 3860
|
||||
3851 PRINT " . ";:GOTO 3861
|
||||
3860 PRINT " ";MID$(Q$,J+1,3);
|
||||
3861 NEXT J
|
||||
3870 ON I GOTO 3880,3900,3910,3920,3930,3940,3950,3960
|
||||
3880 PRINT" Stardate ";
|
||||
3890 TT= T*10 : TT=INT(TT)*0.1:PRINT TT :GOTO 3970
|
||||
3900 PRINT" Condition ";C$:GOTO 3970
|
||||
3910 PRINT" Quadrant ";Q1;",";Q2:GOTO 3970
|
||||
3920 PRINT" Sector ";S1;",";S2:GOTO 3970
|
||||
3930 PRINT" Photon torpedoes ";INT(P):GOTO 3970
|
||||
3940 PRINT" Total energy ";INT(E+S):GOTO 3970
|
||||
3950 PRINT" Shields ";INT(S):GOTO 3970
|
||||
3960 PRINT" Klingons remaining ";INT(K9)
|
||||
3970 NEXT I
|
||||
3971 PRINT "-"*33
|
||||
3972 RETURN
|
||||
3980 REM library computer code
|
||||
3990 CM1$="GALSTATORBASDIRREG"
|
||||
4000 IF D(8)>=0 THEN 4010
|
||||
4001 PRINT"Computer Disabled":GOTO 1520
|
||||
4010 rem KEY 1, "GAL RCD"+CHR$(13)
|
||||
4020 rem KEY 2, "STATUS"+CHR$(13)
|
||||
4030 rem KEY 3, "TOR DATA"+CHR$(13)
|
||||
4040 rem KEY 4, "BASE NAV"+CHR$(13)
|
||||
4050 rem KEY 5, "DIR/DIST"+CHR$(13)
|
||||
4060 rem KEY 6, "REG MAP"+CHR$(13)
|
||||
4070 rem KEY 7,CHR$(13):KEY 8,CHR$(13):KEY 9,CHR$(13):KEY 10,CHR$(13)
|
||||
4071 gosub 4130
|
||||
4080 INPUT"Computer active and awaiting command ";CM$:H8=1
|
||||
4090 FOR K1= 1 TO 6
|
||||
4100 IF MID$(CM$,1,3)<>MID$(CM1$,3*K1-2,3) THEN 4120
|
||||
4110 ON K1 GOTO 4250,4400,4490,4750,4550,4210
|
||||
4120 NEXT K1
|
||||
4121 gosub 4130
|
||||
4122 goto 4080
|
||||
4130 PRINT"Functions available from library-computer:"
|
||||
4140 PRINT" GAL = Cumulative galactic record"
|
||||
4150 PRINT" STA = Status report"
|
||||
4160 PRINT" TOR = Photon torpedo data"
|
||||
4170 PRINT" BAS = Starbase nav data"
|
||||
4180 PRINT" DIR = Direction/distance calculator"
|
||||
4190 PRINT" REG = Galaxy 'region name' map":PRINT
|
||||
4191 return
|
||||
4200 REM setup to change cum gal record to galaxy map
|
||||
4210 H8=0:G5=1:PRINT" the galaxy":GOTO 4290
|
||||
4250 PRINT:PRINT" ";
|
||||
4270 PRINT "Computer record of galaxy for quadrant ";Q1;",";Q2
|
||||
4280 PRINT
|
||||
4290 PRINT" 1 2 3 4 5 6 7 8"
|
||||
4300 O1$=" ----- ----- ----- ----- ----- ----- ----- -----"
|
||||
4310 PRINT O1$
|
||||
4311 FOR I=1 TO 8
|
||||
4312 PRINT I;" ";:IF H8=0 THEN 4350
|
||||
4320 FOR J=1 TO 8
|
||||
4321 PRINT" ";:IF Z(I,J)<>0 THEN 4330
|
||||
4322 PRINT"***";:GOTO 4340
|
||||
4330 ZLEN = len(str$(z(i,j)+1000)
|
||||
4331 PRINT MID$(STR$(Z(I,J)+1000),zlen-2,3);
|
||||
4340 NEXT J
|
||||
4341 GOTO 4370
|
||||
4350 Z4=I:Z5=1
|
||||
4351 GOSUB 5040
|
||||
4352 J0=INT(15-0.5*LEN(G2$)):PRINT TAB(J0);G2$;
|
||||
4360 Z5=5
|
||||
4361 GOSUB 5040
|
||||
4362 J0=INT(40-0.5*LEN(G2$)):PRINT TAB(J0);G2$;
|
||||
4370 PRINT:PRINT O1$
|
||||
4371 NEXT I
|
||||
4372 PRINT
|
||||
4373 rem 'POKE 1229,0 POKE 1237,1
|
||||
4380 GOTO 1520
|
||||
4390 REM status report
|
||||
4400 PRINT" Status Report":X$="":IF K9<=1 THEN 4410
|
||||
4402 X$="s"
|
||||
4410 PRINT"Klingon";X$;" left: ";K9
|
||||
4420 PRINT"Mission must be completed in ";0.1*INT((T0+T9-T)*10);" stardates"
|
||||
4430 X$="s":IF B9>=2 THEN 4440
|
||||
4431 X$="":IF B9<1 THEN 4460
|
||||
4440 PRINT"The federation is maintaining ";B9;" starbase";X$;" in the galaxy"
|
||||
4450 GOTO 3180
|
||||
4460 PRINT"Your stupidity has left you on your own in"
|
||||
4470 PRINT" the galaxy -- you have no starbases left!":GOTO 3180
|
||||
4480 REM torpedo, base nav, d/d calculator
|
||||
4490 IF K3<=0 THEN 2550
|
||||
4500 X$="":IF K3<=1 THEN 4510
|
||||
4501 X$="s"
|
||||
4510 PRINT"From ENTERPRISE to Klingon battle cruiser";X$
|
||||
4520 H8=0:FOR I=1 TO 3
|
||||
4521 IF K(I,3)<=0 THEN 4740
|
||||
4530 W1=K(I,1):X=K(I,2)
|
||||
4540 C1=S1:A=S2:GOTO 4590
|
||||
4550 PRINT"Direction/Distance Calculator:"
|
||||
4560 PRINT"You are at quadrant ";Q1;",";Q2;" sector ";S1;",";S2
|
||||
4570 PRINT"Please enter ":INPUT" initial coordinates (x,y) ";C1,A
|
||||
4580 INPUT" Final coordinates (x,y) ";W1,X
|
||||
4590 X=X-A:A=C1-W1:aa=abs(a):ax=abs(x):IF X<0 THEN 4670
|
||||
4600 IF A<0 THEN 4690
|
||||
4610 IF X>0 THEN 4630
|
||||
4620 IF A<>0 THEN 4630
|
||||
4621 C1=5:GOTO 4640
|
||||
4630 C1=1
|
||||
4640 IF AA<=AX THEN 4660
|
||||
4650 PRINT"Direction1 = ";:cc1=(AA-AX+AA)/AA:PRINT c1+cc1:GOTO 4730
|
||||
4660 PRINT"Direction2 = ";:cc1=C1+(AA/AX):PRINT cc1:GOTO 4730
|
||||
4670 IF A<=0 THEN 4680
|
||||
4671 C1=3:GOTO 4700
|
||||
4680 IF X=0 THEN 4690
|
||||
4681 C1=5:GOTO 4640
|
||||
4690 C1=7
|
||||
4700 IF AA>=AX THEN 4720
|
||||
4710 PRINT"Direction3 = ";:cc1=(AX-AA+AX)/AX:PRINT c1+cc1:GOTO 4730
|
||||
4720 PRINT"Direction4 = ";:CC1=C1+(AX/AA):PRINT CC1
|
||||
4730 PRINT"Distance = ";:cc1=SQR(x*X+A*A):PRINT cc1:IF H8=1 THEN 1520
|
||||
4740 NEXT I
|
||||
4741 GOTO 1520
|
||||
4750 IF B3=0 THEN 4770
|
||||
4752 PRINT"From ENTERPRISE to Starbase:":W1=B4:X=B5
|
||||
4760 GOTO 4540
|
||||
4770 PRINT"Mr. Spock reports, 'Sensors show no starbases in this";
|
||||
4780 PRINT"quadrant.'":GOTO 1520
|
||||
4790 REM find empty place in quadrant (for things)
|
||||
4800 R1=INT(RND(1)*7.98+1.01):R2=INT(RND(1)*7.98+1.01):A$=" ":Z1=R1:Z2=R2
|
||||
4801 GOSUB 4990
|
||||
4802 IF Z3=0 THEN 4800
|
||||
4810 RETURN
|
||||
4820 REM insert in string array for quadrant
|
||||
4830 S8=INT(Z2-0.5)*3+INT(Z1-0.5)*24+1
|
||||
4840 IF LEN(A$)=3 THEN 4850
|
||||
4841 PRINT"ERROR":STOP
|
||||
4850 IF S8<>1 THEN 4860
|
||||
4851 Q$=A$+MID$(Q$,4,189):RETURN
|
||||
4860 IF S8<>190 THEN 4870
|
||||
4861 Q$=MID$(Q$,1,189)+A$:RETURN
|
||||
4870 Q$=MID$(Q$,1,S8-1)+A$+MID$(Q$,s8+3,192-s8-2):RETURN
|
||||
4880 REM prints device name
|
||||
4890 ON R1 GOTO 4900,4910,4920,4930,4940,4950,4960,4970
|
||||
4900 G2$="Warp Engines":RETURN
|
||||
4910 G2$="Short Range Sensors":RETURN
|
||||
4920 G2$="Long Range Sensors":RETURN
|
||||
4930 G2$="Phaser Control":RETURN
|
||||
4940 G2$="Photon Tubes":RETURN
|
||||
4950 G2$="Damage Control":RETURN
|
||||
4960 G2$="Shield Control":RETURN
|
||||
4970 G2$="Library-Computer":RETURN
|
||||
4980 REM string comparison in quadrant array
|
||||
4990 Z1=INT(Z1+0.5):Z2=INT(Z2+0.5):S8=(Z2-1)*3+(Z1-1)*24:Z3=0
|
||||
5000 IF MID$(Q$,S8+1,3)=A$ THEN 5010
|
||||
5001 RETURN
|
||||
5010 Z3=1:RETURN
|
||||
5020 REM quadrant name in g2$ from z4,z5 (=q1,q2)
|
||||
5030 REM call with g5=1 to get region name only
|
||||
5040 IF Z5<=4 THEN 5140
|
||||
5041 ON Z4 GOTO 5060,5070,5080,5090,5100,5110,5120,5130
|
||||
5050 GOTO 5140
|
||||
5060 G2$="Antares":GOTO 5230
|
||||
5070 G2$="Rigel":GOTO 5230
|
||||
5080 G2$="Procyon":GOTO 5230
|
||||
5090 G2$="Vega":GOTO 5230
|
||||
5100 G2$="Canopus":GOTO 5230
|
||||
5110 G2$="Altair":GOTO 5230
|
||||
5120 G2$="Sagittarius":GOTO 5230
|
||||
5130 G2$="Pollux":GOTO 5230
|
||||
5140 ON Z4 GOTO 5150,5160,5170,5180,5180,5200,5210,5220
|
||||
5150 G2$="Sirius":GOTO 5230
|
||||
5160 G2$="Deneb":GOTO 5230
|
||||
5170 G2$="Capella":GOTO 5230
|
||||
5180 G2$="Betelgeuse":GOTO 5230
|
||||
5190 G2$="Aldebaran":GOTO 5230
|
||||
5200 G2$="Regulus":GOTO 5230
|
||||
5210 G2$="Arcturus":GOTO 5230
|
||||
5220 G2$="Spica"
|
||||
5230 IF G5=1 THEN 5240
|
||||
5231 ON Z5 GOTO 5250,5260,5270,5280,5250,5260,5270,5280
|
||||
5240 RETURN
|
||||
5250 G2$=G2$+" i":RETURN
|
||||
5260 G2$=G2$+" ii":RETURN
|
||||
5270 G2$=G2$+" iii":RETURN
|
||||
5280 G2$=G2$+" iv":RETURN
|
||||
5290 rem red alert sound
|
||||
5291 return
|
||||
5300 FOR J= 1 TO 4
|
||||
5310 FOR K=1000 TO 2000 STEP 20
|
||||
5320 rem SOUND K,0.01*18.2
|
||||
5330 NEXT K
|
||||
5340 NEXT J
|
||||
5350 RETURN
|
||||
5360 rem torpedo sound
|
||||
5361 return
|
||||
5370 FOR J = 1500 TO 100 STEP -20
|
||||
5380 rem SOUND J,0.01*18.2
|
||||
5390 rem SOUND 3600-J,.01*18.2
|
||||
5400 NEXT J
|
||||
5410 RETURN
|
||||
5420 rem phaser sound
|
||||
5421 return
|
||||
5430 FOR J= 1 TO 40
|
||||
5440 rem SOUND 800,.01*18.2
|
||||
5450 rem SOUND 2500,.008*18.2
|
||||
5460 NEXT J
|
||||
5470 RETURN
|
||||
5480 rem alarm sound
|
||||
5481 return
|
||||
5490 FOR SI = 1 TO 3
|
||||
5500 FOR J= 800 TO 1500 STEP 20
|
||||
5510 rem SOUND J,.01 *18.2
|
||||
5520 NEXT J
|
||||
5530 FOR K = 1500 TO 800 STEP -20
|
||||
5540 rem SOUND K, .01 *18.2
|
||||
5550 NEXT K
|
||||
5560 NEXT SI
|
||||
5570 RETURN
|
||||
1149
builtin_apps/PyBasic/examples/adventure.bas
Normal file
83
builtin_apps/PyBasic/examples/bagels.bas
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
5 PRINT TAB(33);"BAGELS"
|
||||
10 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY":PRINT:PRINT
|
||||
15 REM *** BAGLES NUMBER GUESSING GAME
|
||||
20 REM *** ORIGINAL SOURCE UNKNOWN BUT SUSPECTED TO BE
|
||||
25 REM *** LAWRENCE HALL OF SCIENCE UC BERKELY
|
||||
30 DIM A1(6),A(3),B(3)
|
||||
40 Y=0:T=255
|
||||
50 PRINT:PRINT:PRINT
|
||||
70 INPUT "WOULD YOU LIKE THE RULES (YES OR NO)";A$
|
||||
90 IF LEFT$(A$,1)="N" THEN 150
|
||||
100 PRINT:PRINT "I AM THINKING OF A THREE-DIGIT NUMBER. TRY TO GUESS"
|
||||
110 PRINT "MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:"
|
||||
120 PRINT " PICO - ONE DIGIT CORRECT BUT IN THE WRONG POSITION"
|
||||
130 PRINT " FERMI - ONE DIGIT CORRECT AND IN THE RIGHT POSITION"
|
||||
140 PRINT " BAGELS - NO DIGITS CORRECT"
|
||||
150 FOR I=1 TO 3
|
||||
160 A(I)=INT(10*RND(1))
|
||||
165 IF I-1=0 THEN 200
|
||||
170 FOR J=1 TO I-1
|
||||
180 IF A(I)=A(J) THEN 160
|
||||
190 NEXT J
|
||||
200 NEXT I
|
||||
210 PRINT:PRINT "O.K. I HAVE A NUMBER IN MIND."
|
||||
220 FOR I=1 TO 20
|
||||
230 PRINT "GUESS #";I
|
||||
240 INPUT A$
|
||||
245 IF LEN(A$)<>3 THEN 630
|
||||
250 FOR Z=1 TO 3
|
||||
251 A1(Z)=ASC(MID$(A$,Z,1))
|
||||
252 NEXT Z
|
||||
260 FOR J=1 TO 3
|
||||
270 IF A1(J)<48 THEN 300
|
||||
280 IF A1(J)>57 THEN 300
|
||||
285 B(J)=A1(J)-48
|
||||
290 NEXT J
|
||||
295 GOTO 320
|
||||
300 PRINT "WHAT?"
|
||||
310 GOTO 230
|
||||
320 IF B(1)=B(2) THEN 650
|
||||
330 IF B(2)=B(3) THEN 650
|
||||
340 IF B(3)=B(1) THEN 650
|
||||
350 C=0:D=0
|
||||
360 FOR J=1 TO 2
|
||||
370 IF A(J)<>B(J+1) THEN 390
|
||||
380 C=C+1
|
||||
390 IF A(J+1)<>B(J) THEN 410
|
||||
400 C=C+1
|
||||
410 NEXT J
|
||||
420 IF A(1)<>B(3) THEN 440
|
||||
430 C=C+1
|
||||
440 IF A(3)<>B(1) THEN 460
|
||||
450 C=C+1
|
||||
460 FOR J=1 TO 3
|
||||
470 IF A(J)<>B(J) THEN 490
|
||||
480 D=D+1
|
||||
490 NEXT J
|
||||
500 IF D=3 THEN 680
|
||||
505 IF C=0 THEN 545
|
||||
520 FOR J=1 TO C
|
||||
530 PRINT "PICO ";
|
||||
540 NEXT J
|
||||
545 IF D=0 THEN 580
|
||||
550 FOR J=1 TO D
|
||||
560 PRINT "FERMI ";
|
||||
570 NEXT J
|
||||
580 IF C+D<>0 THEN 600
|
||||
590 PRINT "BAGELS";
|
||||
600 PRINT
|
||||
605 NEXT I
|
||||
610 PRINT "OH WELL."
|
||||
615 PRINT "THAT'S TWNETY GUESSES. MY NUMBER WAS";100*A(1)+10*A(2)+A(3)
|
||||
620 GOTO 700
|
||||
630 PRINT "TRY GUESSING A THREE-DIGIT NUMBER.":GOTO 230
|
||||
650 PRINT "OH, I FORGOT TO TELL YOU THAT THE NUMBER I HAVE IN MIND"
|
||||
660 PRINT "HAS NO TWO DIGITS THE SAME.":GOTO 230
|
||||
680 PRINT "YOU GOT IT!!!":PRINT
|
||||
690 Y=Y+1
|
||||
700 INPUT "PLAY AGAIN (YES OR NO)";A$
|
||||
720 IF LEFT$(A$,1)="YES" THEN 150
|
||||
730 IF Y=0 THEN 750
|
||||
740 PRINT:PRINT "A";Y;"POINT BAGELS BUFF!!"
|
||||
750 PRINT "HOPE YOU HAD FUN. BYE."
|
||||
999 END
|
||||
234
builtin_apps/PyBasic/examples/eliza.bas
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
5 PRINT TAB ( 16 ) ; "**************************"
|
||||
10 PRINT TAB ( 26 ) ; "ELIZA"
|
||||
20 PRINT TAB ( 20 ) ; "CREATIVE COMPUTING"
|
||||
30 PRINT TAB ( 18 ) ; "MORRISTOWN, NEW JERSEY" : PRINT
|
||||
40 PRINT TAB ( 19 ) ; "ADAPTED FOR IBM PC BY"
|
||||
50 PRINT TAB ( 12 ) ; "PATRICIA DANIELSON AND PAUL HASHFIELD"
|
||||
53 PRINT : PRINT TAB ( 6 ) ; "PLEASE DON'T USE COMMAS OR PERIODS IN YOUR INPUTS" : PRINT
|
||||
55 PRINT TAB ( 16 ) ; "*************************"
|
||||
60 PRINT : PRINT : PRINT
|
||||
80 REM*****INITIALIZATION**********
|
||||
100 DIM S ( 36 ) , R ( 36 ) , N ( 36 )
|
||||
105 DIM KEYWORD$ ( 36 ) , WORDIN$ ( 7 ) , WORDOUT$ ( 7 ) , REPLIES$ ( 112 )
|
||||
107 REM NEED TO PRE-DECLARE BEFORE USE
|
||||
108 P$ = "NULL"
|
||||
110 N1 = 36 : N2 = 14 : N3 = 112
|
||||
112 FOR X = 1 TO N1
|
||||
113 READ TEMP$: KEYWORD$ ( X ) = TEMP$
|
||||
114 NEXT X
|
||||
115 FOR X = 1 TO N2 / 2
|
||||
116 READ TEMP$: WORDIN$ ( X ) = TEMP$ : READ TEMP$: WORDOUT$ ( X ) = TEMP$
|
||||
117 NEXT X
|
||||
118 FOR X = 1 TO N3
|
||||
119 READ TEMP$: REPLIES$ ( X ) = TEMP$
|
||||
120 NEXT X
|
||||
130 FOR X = 1 TO N1
|
||||
140 READ TEMP: S ( X ) = TEMP: READ TEMP: L = TEMP
|
||||
142 R ( X ) = S ( X ) : N ( X ) = S ( X ) + L - 1
|
||||
150 NEXT X
|
||||
160 PRINT "HI! I'M ELIZA. WHAT'S YOUR PROBLEM?"
|
||||
170 REM ***********************************
|
||||
180 REM *******USER INPUT SECTION**********
|
||||
190 REM ***********************************
|
||||
200 INPUT I$
|
||||
201 I$ = " " + I$ + " "
|
||||
210 REM GET RID OF APOSTROPHES
|
||||
220 FOR L = 1 TO LEN ( I$ )
|
||||
230 REM IF MID$(I$,L,1)="'"THEN I$=LEFT$(I$,L-1)+RIGHT$(I$,LEN(I$)-L):GOTO 230
|
||||
240 IF L + 4 > LEN ( I$ ) THEN 250
|
||||
241 IF MID$ ( I$ , L , 4 ) <> "SHUT" THEN 250
|
||||
242 PRINT "O.K. IF YOU FEEL THAT WAY I'LL SHUT UP...."
|
||||
243 END
|
||||
250 NEXT L
|
||||
255 IF I$ = P$ THEN 256 ELSE 260
|
||||
256 PRINT "PLEASE DON'T REPEAT YOURSELF!": GOTO 170
|
||||
260 REM ***********************************
|
||||
270 REM ********FIND KEYWORD IN I$*********
|
||||
280 REM ***********************************
|
||||
300 FOR K = 1 TO N1
|
||||
320 FOR L = 1 TO LEN ( I$ ) - LEN ( KEYWORD$ ( K ) ) + 1
|
||||
340 IF MID$ ( I$ , L , LEN ( KEYWORD$ ( K ) ) ) <> KEYWORD$ ( K ) THEN 350
|
||||
341 IF K <> 13 THEN 349
|
||||
342 IF MID$ ( I$ , L , LEN ( KEYWORD$ ( 29 ) ) ) = KEYWORD$ ( 29 ) THEN 343 ELSE 349
|
||||
343 K = 29
|
||||
349 F$ = KEYWORD$ ( K ) : GOTO 390
|
||||
350 NEXT L
|
||||
360 NEXT K
|
||||
370 K = 36 : GOTO 570 : REM WE DIDN'T FIND ANY KEYWORDS
|
||||
380 REM ******************************************
|
||||
390 REM **TAKE PART OF STRING AND CONJUGATE IT****
|
||||
400 REM **USING THE LIST OF STRINGS TO BE SWAPPED*
|
||||
410 REM ******************************************
|
||||
430 C$ = " " + RIGHT$ ( I$ , LEN ( I$ ) - LEN ( F$ ) - L + 1 ) + " "
|
||||
440 FOR X = 1 TO N2 / 2
|
||||
460 FOR L = 1 TO LEN ( C$ )
|
||||
470 IF L + LEN ( WORDIN$ ( X ) ) > LEN ( C$ ) THEN 510
|
||||
480 IF MID$ ( C$ , L , LEN ( WORDIN$ ( X ) ) ) <> WORDIN$ ( X ) THEN 510
|
||||
490 C$ = LEFT$ ( C$ , L - 1 ) + WORDOUT$ ( X ) + RIGHT$ ( C$ , LEN ( C$ ) - L - LEN ( WORDIN$ ( X ) ) + 1 )
|
||||
495 L = L + LEN ( WORDOUT$ ( X ) )
|
||||
500 GOTO 540
|
||||
510 IF L + LEN ( WORDOUT$ ( X ) ) > LEN ( C$ ) THEN 540
|
||||
520 IF MID$ ( C$ , L , LEN ( WORDOUT$ ( X ) ) ) <> WORDOUT$ ( X ) THEN 540
|
||||
530 C$ = LEFT$ ( C$ , L - 1 ) + WORDIN$ ( X ) + RIGHT$ ( C$ , LEN ( C$ ) - L - LEN ( WORDOUT$ ( X ) ) + 1 )
|
||||
535 L = L + LEN ( WORDIN$ ( X ) )
|
||||
540 NEXT L
|
||||
550 NEXT X
|
||||
551 IF MID$ ( C$ , 2 , 1 ) = " " THEN 552 ELSE 555
|
||||
552 C$ = RIGHT$ ( C$ , LEN ( C$ ) - 1 ) : REM ONLY 1 SPACE
|
||||
555 FOR L = 1 TO LEN ( C$ )
|
||||
556 IF MID$ ( C$ , L , 1 ) = "!" THEN 557 ELSE 558
|
||||
557 C$ = LEFT$ ( C$ , L - 1 ) + RIGHT$ ( C$ , LEN ( C$ ) - L ) : GOTO 556
|
||||
558 NEXT L
|
||||
560 REM **********************************************
|
||||
570 REM **NOW USING THE KEYWORD NUMBER (K) GET REPLY**
|
||||
580 REM **********************************************
|
||||
600 F$ = REPLIES$ ( R ( K ) )
|
||||
610 R ( K ) = R ( K ) + 1
|
||||
615 IF R ( K ) > N ( K ) THEN 617 ELSE 620
|
||||
617 R ( K ) = S ( K )
|
||||
620 IF RIGHT$ ( F$ , 1 ) <> "*" THEN 621 ELSE 625
|
||||
621 PRINT F$ : P$ = I$ : GOTO 170
|
||||
625 IF C$ <> " " THEN 630
|
||||
626 PRINT "YOU WILL HAVE TO ELABORATE MORE FOR ME TO HELP YOU"
|
||||
627 GOTO 170
|
||||
630 PRINT LEFT$ ( F$ , LEN ( F$ ) - 1 ) ; C$
|
||||
640 P$ = I$ : GOTO 170
|
||||
1000 REM *******************************
|
||||
1010 REM *****PROGRAM DATA FOLLOWS******
|
||||
1020 REM *******************************
|
||||
1030 REM *********KEYWORDS**************
|
||||
1049 REM *******************************
|
||||
1050 DATA "CAN YOU " , "CAN I " , "YOU ARE " , "YOU'RE " , "I DON'T " , "I FEEL "
|
||||
1060 DATA "WHY DON'T YOU " , "WHY CAN'T I " , "ARE YOU " , "I CAN'T " , "I AM " , "I'M "
|
||||
1070 DATA "YOU " , "I WANT " , "WHAT " , "HOW " , "WHO " , "WHERE " , "WHEN " , "WHY "
|
||||
1080 DATA "NAME " , "CAUSE " , "SORRY " , "DREAM " , "HELLO " , "HI " , "MAYBE "
|
||||
1090 DATA "NO" , "YOUR " , "ALWAYS " , "THINK " , "ALIKE " , "YES " , "FRIEND "
|
||||
1100 DATA "COMPUTER" , "NOKEYFOUND"
|
||||
1200 REM *********************************
|
||||
1210 REM ***STRING DATA FOR CONJUGATIONS**
|
||||
1220 REM *********************************
|
||||
1230 DATA " ARE " , " AM " , " WERE " , " WAS " , " YOU " , " I " , " YOUR" , " MY "
|
||||
1235 DATA " I'VE " , " YOU'VE " , " I'M " , " YOU'RE "
|
||||
1240 DATA " ME " , " YOU "
|
||||
1300 REM ******************************
|
||||
1310 REM *********REPLIES**************
|
||||
1320 REM ******************************
|
||||
1330 DATA "DON'T YOU BELIEVE THAT I CAN*"
|
||||
1340 DATA "PERHAPS YOU WOULD LIKE TO BE LIKE ME*"
|
||||
1350 DATA "YOU WANT ME TO BE ABLE TO*"
|
||||
1360 DATA "PERHAPS YOU DON'T WANT TO*"
|
||||
1365 DATA "DO YOU WANT TO BE ABLE TO*"
|
||||
1370 DATA "WHAT MAKES YOU THINK I AM*"
|
||||
1380 DATA "DOES IT PLEASE YOU TO BELIEVE I AM*"
|
||||
1390 DATA "PERHAPS YOU WOULD LIKE TO BE*"
|
||||
1400 DATA "DO YOU SOMETIMES WISH YOU WERE*"
|
||||
1410 DATA "DON'T YOU REALLY*"
|
||||
1420 DATA "WHY DON'T YOU*"
|
||||
1430 DATA "DO YOU WISH TO BE ABLE TO*"
|
||||
1440 DATA "DOES THAT TROUBLE YOU*"
|
||||
1450 DATA "DO YOU OFTEN FEEL*"
|
||||
1460 DATA "DO YOU OFTEN FEEL*"
|
||||
1470 DATA "DO YOU ENJOY FEELING*"
|
||||
1480 DATA "DO YOU REALLY BELIEVE I DON'T*"
|
||||
1490 DATA "PERHAPS IN GOOD TIME I WILL*"
|
||||
1500 DATA "DO YOU WANT ME TO*"
|
||||
1510 DATA "DO YOU THINK YOU SHOULD BE ABLE TO*"
|
||||
1520 DATA "WHY CAN'T YOU*"
|
||||
1530 DATA "WHY ARE YOU INTERESTED IN WHETHER OR NOT I AM*"
|
||||
1540 DATA "WOULD YOU PREFER IF I WERE NOT*"
|
||||
1550 DATA "PERHAPS IN YOUR FANTASIES I AM*"
|
||||
1560 DATA "HOW DO YOU KNOW YOU CAN'T*"
|
||||
1570 DATA "HAVE YOU TRIED?"
|
||||
1580 DATA "PERHAPS YOU CAN NOW*"
|
||||
1590 DATA "DID YOU COME TO ME BECAUSE YOU ARE*"
|
||||
1600 DATA "HOW LONG HAVE YOU BEEN*"
|
||||
1610 DATA "DO YOU BELIEVE IT IS NORMAL TO BE*"
|
||||
1620 DATA "DO YOU ENJOY BEING*"
|
||||
1630 DATA "WE WERE DISCUSSING YOU--NOT ME."
|
||||
1640 DATA "OH, I*"
|
||||
1650 DATA "YOU'RE NOT REALLY TALKING ABOUT ME, ARE YOU?"
|
||||
1660 DATA "WHAT WOULD IT MEAN TO YOU IF YOU GOT*"
|
||||
1670 DATA "WHY DO YOU WANT*"
|
||||
1680 DATA "SUPPOSE YOU SOON GOT*"
|
||||
1690 DATA "WHAT IF YOU NEVER GOT*"
|
||||
1700 DATA "I SOMETIMES ALSO WANT*"
|
||||
1710 DATA "WHY DO YOU ASK?"
|
||||
1720 DATA "DOES THAT QUESTION INTEREST YOU?"
|
||||
1730 DATA "WHAT ANSWER WOULD PLEASE YOU THE MOST?"
|
||||
1740 DATA "WHAT DO YOU THINK?"
|
||||
1750 DATA "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?"
|
||||
1760 DATA "WHAT IS IT THAT YOU REALLY WANT TO KNOW?"
|
||||
1770 DATA "HAVE YOU ASKED ANYONE ELSE?"
|
||||
1780 DATA "HAVE YOU ASKED SUCH QUESTIONS BEFORE?"
|
||||
1790 DATA "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"
|
||||
1800 DATA "NAMES DON'T INTEREST ME."
|
||||
1810 DATA "I DON'T CARE ABOUT NAMES --PLEASE GO ON."
|
||||
1820 DATA "IS THAT THE REAL REASON?"
|
||||
1830 DATA "DON'T ANY OTHER REASONS COME TO MIND?"
|
||||
1840 DATA "DOES THAT REASON EXPLAIN ANYTHING ELSE?"
|
||||
1850 DATA "WHAT OTHER REASONS MIGHT THERE BE?"
|
||||
1860 DATA "PLEASE DON'T APOLOGIZE!"
|
||||
1870 DATA "APOLOGIES ARE NOT NECESSARY."
|
||||
1880 DATA "WHAT FEELINGS DO YOU HAVE WHEN YOU APOLOGIZE?"
|
||||
1890 DATA "DON'T BE SO DEFENSIVE!"
|
||||
1900 DATA "WHAT DOES THAT DREAM SUGGEST TO YOU?"
|
||||
1910 DATA "DO YOU DREAM OFTEN?"
|
||||
1920 DATA "WHAT PERSONS APPEAR IN YOUR DREAMS?"
|
||||
1930 DATA "ARE YOU DISTURBED BY YOUR DREAMS?"
|
||||
1940 DATA "HOW DO YOU DO ...PLEASE STATE YOUR PROBLEM."
|
||||
1950 DATA "YOU DON'T SEEM QUITE CERTAIN."
|
||||
1960 DATA "WHY THE UNCERTAIN TONE?"
|
||||
1970 DATA "CAN'T YOU BE MORE POSITIVE?"
|
||||
1980 DATA "YOU AREN'T SURE?"
|
||||
1990 DATA "DON'T YOU KNOW?"
|
||||
2000 DATA "ARE YOU SAYING NO JUST TO BE NEGATIVE?"
|
||||
2010 DATA "YOU ARE BEING A BIT NEGATIVE."
|
||||
2020 DATA "WHY NOT?"
|
||||
2030 DATA "ARE YOU SURE?"
|
||||
2040 DATA "WHY NO?"
|
||||
2050 DATA "WHY ARE YOU CONCERNED ABOUT MY*"
|
||||
2060 DATA "WHAT ABOUT YOUR OWN*"
|
||||
2070 DATA "CAN YOU THINK OF A SPECIFIC EXAMPLE?"
|
||||
2080 DATA "WHEN?"
|
||||
2090 DATA "WHAT ARE YOU THINKING OF?"
|
||||
2100 DATA "REALLY, ALWAYS?"
|
||||
2110 DATA "DO YOU REALLY THINK SO?"
|
||||
2120 DATA "BUT YOU ARE NOT SURE YOU*"
|
||||
2130 DATA "DO YOU DOUBT YOU*"
|
||||
2140 DATA "IN WHAT WAY?"
|
||||
2150 DATA "WHAT RESEMBLANCE DO YOU SEE?"
|
||||
2160 DATA "WHAT DOES THE SIMILARITY SUGGEST TO YOU?"
|
||||
2170 DATA "WHAT OTHER CONNECTIONS DO YOU SEE?"
|
||||
2180 DATA "COULD THERE REALLY BE SOME CONNECTION?"
|
||||
2190 DATA "HOW?"
|
||||
2200 DATA "YOU SEEM QUITE POSITIVE."
|
||||
2210 DATA "ARE YOU SURE?"
|
||||
2220 DATA "I SEE."
|
||||
2230 DATA "I UNDERSTAND."
|
||||
2240 DATA "WHY DO YOU BRING UP THE TOPIC OF FRIENDS?"
|
||||
2250 DATA "DO YOUR FRIENDS WORRY YOU?"
|
||||
2260 DATA "DO YOUR FRIENDS PICK ON YOU?"
|
||||
2270 DATA "ARE YOU SURE YOU HAVE ANY FRIENDS?"
|
||||
2280 DATA "DO YOU IMPOSE ON YOUR FRIENDS?"
|
||||
2290 DATA "PERHAPS YOUR LOVE FOR FRIENDS WORRIES YOU."
|
||||
2300 DATA "DO COMPUTERS WORRY YOU?"
|
||||
2310 DATA "ARE YOU TALKING ABOUT ME IN PARTICULAR?"
|
||||
2320 DATA "ARE YOU FRIGHTENED BY MACHINES?"
|
||||
2330 DATA "WHY DO YOU MENTION COMPUTERS?"
|
||||
2340 DATA "WHAT DO YOU THINK MACHINES HAVE TO DO WITH YOUR PROBLEM?"
|
||||
2350 DATA "DON'T YOU THINK COMPUTERS CAN HELP PEOPLE?"
|
||||
2360 DATA "WHAT IS IT ABOUT MACHINES THAT WORRIES YOU?"
|
||||
2370 DATA "SAY, DO YOU HAVE ANY PSYCHOLOGICAL PROBLEMS?"
|
||||
2380 DATA "WHAT DOES THAT SUGGEST TO YOU?"
|
||||
2390 DATA "I SEE."
|
||||
2400 DATA "I'M NOT SURE I UNDERSTAND YOU FULLY."
|
||||
2410 DATA "COME COME ELUCIDATE YOUR THOUGHTS."
|
||||
2420 DATA "CAN YOU ELABORATE ON THAT?"
|
||||
2430 DATA "THAT IS QUITE INTERESTING."
|
||||
2500 REM *************************
|
||||
2510 REM *****DATA FOR FINDING RIGHT REPLIES
|
||||
2520 REM *************************
|
||||
2530 DATA 1 , 3 , 4 , 2 , 6 , 4 , 6 , 4 , 10 , 4 , 14 , 3 , 17 , 3 , 20 , 2 , 22 , 3 , 25 , 3
|
||||
2540 DATA 28 , 4 , 28 , 4 , 32 , 3 , 35 , 5 , 40 , 9 , 40 , 9 , 40 , 9 , 40 , 9 , 40 , 9 , 40 , 9
|
||||
2550 DATA 49 , 2 , 51 , 4 , 55 , 4 , 59 , 4 , 63 , 1 , 63 , 1 , 64 , 5 , 69 , 5 , 74 , 2 , 76 , 4
|
||||
2560 DATA 80 , 3 , 83 , 7 , 90 , 3 , 93 , 6 , 99 , 7 , 106 , 6
|
||||
9
builtin_apps/PyBasic/examples/factorial.bas
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
10 REM A SHORT PROGRAM TO CALCULATE FACTORIAL OF
|
||||
20 REM SUPPLIED NUMBER N
|
||||
30 INPUT "Please provide N: "; N
|
||||
35 REM OUTPUT IS NOW A RESERVED KEYWORD
|
||||
40 OUTPUTVAL = 1
|
||||
50 FOR I = 1 TO N
|
||||
60 OUTPUTVAL = OUTPUTVAL * I
|
||||
70 NEXT I
|
||||
80 PRINT "Factorial of N is "; OUTPUTVAL
|
||||
94
builtin_apps/PyBasic/examples/life.bas
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
2 PRINT TAB(34);"LIFE"
|
||||
4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN NEW JERSEY"
|
||||
6 PRINT: PRINT: PRINT
|
||||
8 PRINT "ENTER YOUR PATTERN, AS A SERIES OF SPACES"
|
||||
9 PRINT "AND STARS, LINE BY LINE. FINISH BY TYPING"
|
||||
10 PRINT "'DONE' ON THE FINAL LINE:"
|
||||
11 X1=1: Y1=1: X2=24: Y2=70: G=0: P=0
|
||||
12 DIM A(24, 70): DIM B$(24)
|
||||
15 I9=1
|
||||
20 C=1
|
||||
30 INPUT TEMP$
|
||||
35 B$(C) = TEMP$
|
||||
40 IF B$(C)="DONE" THEN 45 ELSE 50
|
||||
45 B$(C)="": GOTO 80
|
||||
46 REM ORIGINAL VERSION REQUIRED USER TO WRITE
|
||||
47 REM DOTS INSTEAD OF SPACES, WHICH WOULD THEN BE
|
||||
48 REM CONVERTED TO SPACES
|
||||
50 REM IF LEFT$(B$(C), 1)="." THEN 55 ELSE 60
|
||||
55 REM B$(C)=" "+RIGHT$(B$(C), LEN(B$(C))-1)
|
||||
60 C=C+1
|
||||
70 GOTO 30
|
||||
80 C=C-1: L=0
|
||||
90 FOR X=1 TO C-1
|
||||
100 IF LEN(B$(X))>L THEN 105 ELSE 110
|
||||
105 L=LEN(B$(X))
|
||||
110 NEXT X
|
||||
120 X1=INT(11-C/2)
|
||||
130 Y1=INT(33-L/2)
|
||||
135 REM TRANSCRIBE INPUT PATTERN INTO ARRAY
|
||||
140 FOR X=1 TO C
|
||||
150 FOR Y=1 TO LEN(B$(X))
|
||||
160 IF MID$(B$(X), Y, 1)<>" " THEN 165 ELSE 170
|
||||
165 A(X1+X, Y1+Y)=1: P=P+1
|
||||
170 NEXT Y
|
||||
180 NEXT X
|
||||
200 PRINT: PRINT: PRINT
|
||||
210 PRINT "GENERATION: "; G; " POPULATION: "; P;
|
||||
212 IF I9=0 THEN 213 ELSE 215
|
||||
213 PRINT " INVALID!";
|
||||
215 X3=24: Y3=70: X4=1: Y4=1: P=0
|
||||
220 G=G+1
|
||||
230 FOR X=X1 TO X2
|
||||
240 PRINT
|
||||
250 FOR Y=Y1 TO Y2
|
||||
253 IF A(X, Y)=2 THEN 254 ELSE 256
|
||||
254 A(X, Y)=0: GOTO 270
|
||||
256 IF A(X, Y)=3 THEN 258 ELSE 260
|
||||
258 A(X, Y)=1: GOTO 261
|
||||
260 IF A(X, Y)<>1 THEN 270
|
||||
261 PRINT TAB(Y);"*";
|
||||
262 IF X<X3 THEN 263 ELSE 264
|
||||
263 X3=X
|
||||
264 IF X>X4 THEN 265 ELSE 266
|
||||
265 X4=X
|
||||
266 IF Y<Y3 THEN 267 ELSE 268
|
||||
267 Y3=Y
|
||||
268 IF Y>Y4 THEN 269 ELSE 270
|
||||
269 Y4=Y
|
||||
270 NEXT Y
|
||||
290 NEXT X
|
||||
295 FOR X=X2+1 TO 24
|
||||
296 PRINT
|
||||
297 NEXT X
|
||||
299 X1=X3: X2=X4: Y1=Y3: Y2=Y4
|
||||
301 IF X1<3 THEN 302 ELSE 303
|
||||
302 X1=3: I9=-1
|
||||
303 IF X2>22 THEN 304 ELSE 305
|
||||
304 X2=22: I9=-1
|
||||
305 IF Y1<3 THEN 306 ELSE 307
|
||||
306 Y1=3: I9=-1
|
||||
307 IF Y2>68 THEN 308 ELSE 309
|
||||
308 Y2=68: I9=-1
|
||||
309 P=0
|
||||
500 FOR X=X1-1 TO X2+1
|
||||
510 FOR Y=Y1-1 TO Y2+1
|
||||
520 C=0
|
||||
530 FOR I=X-1 TO X+1
|
||||
540 FOR J=Y-1 TO Y+1
|
||||
550 IF A(I, J)=1 OR A(I, J)=2 THEN 555 ELSE 560
|
||||
555 C=C+1
|
||||
560 NEXT J
|
||||
570 NEXT I
|
||||
580 IF A(X, Y)=0 THEN 610
|
||||
590 IF C<3 or C>4 THEN 592 ELSE 595
|
||||
592 A(X, Y)=2: GOTO 600
|
||||
595 P=P+1
|
||||
600 GOTO 620
|
||||
610 IF C=3 THEN 615 ELSE 620
|
||||
615 A(X, Y)=3: P=P+1
|
||||
620 NEXT Y
|
||||
630 NEXT X
|
||||
635 X1=X1-1: Y1=Y1-1: X2=X2+1: Y2=Y2+1
|
||||
640 GOTO 210
|
||||
650 END
|
||||
720
builtin_apps/PyBasic/examples/oregon.bas
Normal file
|
|
@ -0,0 +1,720 @@
|
|||
10 REM PROGRAM NAME - OREGON VERSION:01/01/78
|
||||
20 REM ORIGINAL PROGRAMMING BY BILL HEINEMANN - 1971
|
||||
30 REM SUPPORT RESEARCH AND MATERIALS BY DON RAVITSCH,
|
||||
40 REM MINNESOTA EDUCATIONAL COMPUTING CONSORTIUM STAFF
|
||||
50 REM CDC CYBER 70/73-26 BASIC 3.1
|
||||
60 REM DOCUMENTATION BOOKLET 'OREGON' AVAILABLE FROM
|
||||
61 REM MECC SUPPORT SERVICES
|
||||
62 REM 2520 BROADWAY DRIVE
|
||||
63 REM ST. PAUL MN 55113
|
||||
80 REM
|
||||
150 REM *FOR THE MEANING OF THE VARIABLES USED, LIST LINES 6470-6790*
|
||||
155 REM
|
||||
160 PRINT "DO YOU NEED INSTRUCTIONS (YES/NO)" ;
|
||||
165 REM PYBASIC CAN'T DO STRING OPERATIONS ON ARRAYS
|
||||
170 REM DIM C$(5)
|
||||
180 REM RANDOMIZE REMOVED
|
||||
190 INPUT C$
|
||||
200 IF C$ = "NO" THEN 690
|
||||
210 PRINT
|
||||
220 PRINT
|
||||
230 REM ***INSTRUCTIONS***
|
||||
240 PRINT "THIS PROGRAM SIMULATES A TRIP OVER THE OREGON TRAIL FROM"
|
||||
250 PRINT "INDEPENDENCE, MISSOURI TO OREGON CITY, OREGON IN 1847."
|
||||
260 PRINT "YOUR FAMILY OF FIVE WILL COVER THE 2040 MILE OREGON TRAIL"
|
||||
270 PRINT "IN 5-6 MONTHS --- IF YOU MAKE IT ALIVE."
|
||||
280 PRINT
|
||||
290 PRINT "YOU HAD SAVED $900 TO SPEND FOR THE TRIP, AND YOU'VE JUST"
|
||||
300 PRINT " PAID $200 FOR A WAGON."
|
||||
310 PRINT "YOU WILL NEED TO SPEND THE REST OF YOUR MONEY ON THE"
|
||||
320 PRINT " FOLLOWING ITEMS:"
|
||||
330 PRINT
|
||||
340 PRINT " OXEN - YOU CAN SPEND $200-$300 ON YOUR TEAM"
|
||||
350 PRINT " THE MORE YOU SPEND, THE FASTER YOU'LL GO"
|
||||
360 PRINT " BECAUSE YOU'LL HAVE BETTER ANIMALS"
|
||||
370 PRINT
|
||||
380 PRINT " FOOD - THE MORE YOU HAVE, THE LESS CHANCE THERE"
|
||||
390 PRINT " IS OF GETTING SICK"
|
||||
400 PRINT
|
||||
410 PRINT " AMMUNITION - $1 BUYS A BELT OF 50 BULLETS"
|
||||
420 PRINT " YOU WILL NEED BULLETS FOR ATTACKS BY ANIMALS"
|
||||
430 PRINT " AND BANDITS, AND FOR HUNTING FOOD"
|
||||
440 PRINT
|
||||
450 PRINT " CLOTHING - THIS IS ESPECIALLY IMPORTANT FOR THE COLD"
|
||||
460 PRINT " WEATHER YOU WILL ENCOUNTER WHEN CROSSING"
|
||||
470 PRINT " THE MOUNTAINS"
|
||||
480 PRINT
|
||||
490 PRINT " MISCELLANEOUS SUPPLIES - THIS INCLUDES MEDICINE AND"
|
||||
500 PRINT " OTHER THINGS YOU WILL NEED FOR SICKNESS"
|
||||
510 PRINT " AND EMERGENCY REPAIRS"
|
||||
520 PRINT
|
||||
530 PRINT
|
||||
540 PRINT "YOU CAN SPEND ALL YOUR MONEY BEFORE YOU START YOUR TRIP -"
|
||||
550 PRINT "OR YOU CAN SAVE SOME OF YOUR CASH TO SPEND AT FORTS ALONG"
|
||||
560 PRINT "THE WAY WHEN YOU RUN LOW. HOWEVER, ITEMS COST MORE AT"
|
||||
570 PRINT "THE FORTS. YOU CAN ALSO GO HUNTING ALONG THE WAY TO GET"
|
||||
580 PRINT "MORE FOOD."
|
||||
590 PRINT "WHENEVER YOU HAVE TO USE YOUR TRUSTY RIFLE ALONG THE WAY,"
|
||||
600 PRINT "YOU WILL BE TOLD TO TYPE IN A WORD (ONE THAT SOUNDS LIKE A "
|
||||
610 PRINT "GUN SHOT). THE FASTER YOU TYPE IN THAT WORD AND HIT THE"
|
||||
620 PRINT "" "RETURN" " KEY, THE BETTER LUCK YOU'LL HAVE WITH YOUR GUN."
|
||||
630 PRINT
|
||||
640 PRINT "AT EACH TURN, ALL ITEMS ARE SHOWN IN DOLLAR AMOUNTS"
|
||||
650 PRINT "EXCEPT BULLETS"
|
||||
660 PRINT "WHEN ASKED TO ENTER MONEY AMOUNTS, DON'T USE A " "$" "."
|
||||
670 PRINT
|
||||
680 PRINT "GOOD LUCK!!!"
|
||||
690 PRINT
|
||||
700 PRINT
|
||||
710 PRINT "HOW GOOD A SHOT ARE YOU WITH YOUR RIFLE?"
|
||||
720 PRINT " (1) ACE MARKSMAN, (2) GOOD SHOT, (3) FAIR TO MIDDLIN'"
|
||||
730 PRINT " (4) NEED MORE PRACTICE, (5) SHAKY KNEES"
|
||||
740 PRINT "ENTER ONE OF THE ABOVE -- THE BETTER YOU CLAIM YOU ARE, THE"
|
||||
750 PRINT "FASTER YOU'LL HAVE TO BE WITH YOUR GUN TO BE SUCCESSFUL."
|
||||
760 INPUT D9
|
||||
770 IF D9 > 5 THEN 790
|
||||
780 GOTO 810
|
||||
790 D9 = 0
|
||||
800 REM ***INITIAL PURCHASES***
|
||||
810 X1 = - 1
|
||||
820 REM LINES 820-829 MODIFIED BY CHRISTOPHER PEDERSEN (AUG 10, 2018)
|
||||
821 REM FOR COMPATIBILITY WITH THE CHIPMUNK BASIC INTERPRETER
|
||||
822 REM CHIPMUNK BASIC: http://www.nicholson.com/rhn/basic/
|
||||
823 D3 = 0
|
||||
824 M9 = 0
|
||||
825 M = 0
|
||||
826 F2 = 0
|
||||
827 F1 = 0
|
||||
828 S4 = 0
|
||||
829 K8 = 0
|
||||
830 PRINT
|
||||
840 PRINT
|
||||
850 PRINT "HOW MUCH DO YOU WANT TO SPEND ON YOUR OXEN TEAM" ;
|
||||
860 INPUT A
|
||||
870 IF A >= 200 THEN 900
|
||||
880 PRINT "NOT ENOUGH"
|
||||
890 GOTO 850
|
||||
900 IF A <= 300 THEN 930
|
||||
910 PRINT "TOO MUCH"
|
||||
920 GOTO 850
|
||||
930 PRINT "HOW MUCH DO YOU WANT TO SPEND ON FOOD" ;
|
||||
940 INPUT F
|
||||
950 IF F >= 0 THEN 980
|
||||
960 PRINT "IMPOSSIBLE"
|
||||
970 GOTO 930
|
||||
980 PRINT "HOW MUCH DO YOU WANT TO SPEND ON AMMUNITION" ;
|
||||
990 INPUT B
|
||||
1000 IF B >= 0 THEN 1030
|
||||
1010 PRINT "IMPOSSIBLE"
|
||||
1020 GOTO 980
|
||||
1030 PRINT "HOW MUCH DO YOU WANT TO SPEND ON CLOTHING" ;
|
||||
1040 INPUT C
|
||||
1050 IF C >= 0 THEN 1080
|
||||
1060 PRINT "IMPOSSIBLE"
|
||||
1070 GOTO 1030
|
||||
1080 PRINT "HOW MUCH DO YOU WANT TO SPEND ON MISCELLANEOUS SUPPLIES" ;
|
||||
1090 INPUT M1
|
||||
1100 IF M1 >= 0 THEN 1130
|
||||
1110 PRINT "IMPOSSIBLE"
|
||||
1120 GOTO 1080
|
||||
1130 T = 700 - A - F - B - C - M1
|
||||
1140 IF T >= 0 THEN 1170
|
||||
1150 PRINT "YOU OVERSPENT--YOU ONLY HAD $700 TO SPEND. BUY AGAIN"
|
||||
1160 GOTO 830
|
||||
1170 B = 50 * B
|
||||
1180 PRINT "AFTER ALL YOUR PURCHASES. YOU NOW HAVE " ; T ; " DOLLARS LEFT"
|
||||
1190 PRINT
|
||||
1200 PRINT "MONDAY MARCH 29 1847"
|
||||
1210 PRINT
|
||||
1220 GOTO 1750
|
||||
1230 IF M >= 2040 THEN 5430
|
||||
1240 REM ***SETTING DATE***
|
||||
1250 D3 = D3 + 1
|
||||
1260 PRINT
|
||||
1270 PRINT "MONDAY " ;
|
||||
1280 IF D3 > 10 THEN 1300
|
||||
1290 ON D3 GOTO 1310 , 1330 , 1350 , 1370 , 1390 , 1410 , 1430 , 1450 , 1470 , 1490
|
||||
1300 ON D3 - 10 GOTO 1510 , 1530 , 1550 , 1570 , 1590 , 1610 , 1630 , 1650 , 1670 , 1690
|
||||
1310 PRINT "APRIL 12 " ;
|
||||
1320 GOTO 1720
|
||||
1330 PRINT "APRIL 26 " ;
|
||||
1340 GOTO 1720
|
||||
1350 PRINT "MAY 10 " ;
|
||||
1360 GOTO 1720
|
||||
1370 PRINT "MAY 24 " ;
|
||||
1380 GOTO 1720
|
||||
1390 PRINT "JUNE 7 " ;
|
||||
1400 GOTO 1720
|
||||
1410 PRINT "JUNE 21 " ;
|
||||
1420 GOTO 1720
|
||||
1430 PRINT "JULY 5 " ;
|
||||
1440 GOTO 1720
|
||||
1450 PRINT "JULY 19 " ;
|
||||
1460 GOTO 1720
|
||||
1470 PRINT "AUGUST 2 " ;
|
||||
1480 GOTO 1720
|
||||
1490 PRINT "AUGUST 16 " ;
|
||||
1500 GOTO 1720
|
||||
1510 PRINT "AUGUST 31 " ;
|
||||
1520 GOTO 1720
|
||||
1530 PRINT "SEPTEMBER 13 " ;
|
||||
1540 GOTO 1720
|
||||
1550 PRINT "SEPTEMBER 27 " ;
|
||||
1560 GOTO 1720
|
||||
1570 PRINT "OCTOBER 11 " ;
|
||||
1580 GOTO 1720
|
||||
1590 PRINT "OCTOBER 25 " ;
|
||||
1600 GOTO 1720
|
||||
1610 PRINT "NOVEMBER 8 " ;
|
||||
1620 GOTO 1720
|
||||
1630 PRINT "NOVEMBER 22 " ;
|
||||
1640 GOTO 1720
|
||||
1650 PRINT "DECEMBER 6 " ;
|
||||
1660 GOTO 1720
|
||||
1670 PRINT "DECEMBER 20 " ;
|
||||
1680 GOTO 1720
|
||||
1690 PRINT "YOU HAVE BEEN ON THE TRAIL TOO LONG ------"
|
||||
1700 PRINT "YOUR FAMILY DIES IN THE FIRST BLIZZARD OF WINTER"
|
||||
1710 GOTO 5170
|
||||
1720 PRINT "1847"
|
||||
1730 PRINT
|
||||
1740 REM ***BEGINNING EACH TURN***
|
||||
1750 IF F >= 0 THEN 1770
|
||||
1760 F = 0
|
||||
1770 IF B >= 0 THEN 1790
|
||||
1780 B = 0
|
||||
1790 IF C >= 0 THEN 1810
|
||||
1800 C = 0
|
||||
1810 IF M1 >= 0 THEN 1830
|
||||
1820 M1 = 0
|
||||
1830 IF F >= 13 THEN 1850
|
||||
1840 PRINT "YOU'D BETTER DO SOME HUNTING OR BUY FOOD AND SOON!!!!"
|
||||
1850 F = INT ( F )
|
||||
1860 B = INT ( B )
|
||||
1870 C = INT ( C )
|
||||
1880 M1 = INT ( M1 )
|
||||
1890 T = INT ( T )
|
||||
1900 M = INT ( M )
|
||||
1910 M2 = M
|
||||
1920 IF S4 = 1 THEN 1950
|
||||
1930 IF K8 = 1 THEN 1950
|
||||
1940 GOTO 1990
|
||||
1950 T = T - 20
|
||||
1960 IF T < 0 THEN 5080
|
||||
1970 PRINT "DOCTOR'S BILL IS $20"
|
||||
1980 REM LINES 1980-1982 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH MODERN BASIC
|
||||
1981 LET S4 = 0
|
||||
1982 LET K8 = S4
|
||||
1990 IF M9 = 1 THEN 2020
|
||||
2000 PRINT "TOTAL MILEAGE: " ; M
|
||||
2010 GOTO 2040
|
||||
2020 PRINT "TOTAL MILEAGE: 950"
|
||||
2030 M9 = 0
|
||||
2040 REM LINES 2040-2050 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH MODERN BASIC
|
||||
2041 PRINT "FOOD: " ; F
|
||||
2042 PRINT "BULLETS: " ; B
|
||||
2043 PRINT "CLOTHING: " ; C
|
||||
2044 PRINT "MISC. SUPPLIES: " ; M1
|
||||
2050 PRINT "CASH: $" ; T
|
||||
2060 IF X1 = - 1 THEN 2170
|
||||
2070 X1 = X1 * ( - 1 )
|
||||
2080 PRINT "DO YOU WANT TO (1) STOP AT THE NEXT FORT, (2) HUNT, " ;
|
||||
2090 PRINT "OR (3) CONTINUE"
|
||||
2100 INPUT X
|
||||
2110 REM IF X>2 THEN 2150
|
||||
2120 REM IF X<1 THEN 2150
|
||||
2130 REM LET X=INT(X)
|
||||
2140 GOTO 2270
|
||||
2150 LET X = 3
|
||||
2160 GOTO 2270
|
||||
2170 PRINT "DO YOU WANT TO (1) HUNT, OR (2) CONTINUE"
|
||||
2180 INPUT X
|
||||
2190 IF X = 1 THEN 2210
|
||||
2200 LET X = 2
|
||||
2210 LET X = X + 1
|
||||
2220 IF X = 3 THEN 2260
|
||||
2230 IF B > 39 THEN 2260
|
||||
2240 PRINT "TOUGH---YOU NEED MORE BULLETS TO GO HUNTING"
|
||||
2250 GOTO 2170
|
||||
2260 X1 = X1 * ( - 1 )
|
||||
2270 ON X GOTO 2290 , 2540 , 2720
|
||||
2280 REM ***STOPPING AT FORT***
|
||||
2290 PRINT "ENTER WHAT YOU WISH TO SPEND ON THE FOLLOWING"
|
||||
2300 PRINT "FOOD" ;
|
||||
2310 GOSUB 2330
|
||||
2320 GOTO 2410
|
||||
2330 INPUT P
|
||||
2340 IF P < 0 THEN 2400
|
||||
2350 T = T - P
|
||||
2360 IF T >= 0 THEN 2400
|
||||
2370 PRINT "YOU DON'T HAVE THAT MUCH--KEEP YOUR SPENDING DOWN"
|
||||
2380 T = T + P
|
||||
2390 P = 0
|
||||
2400 RETURN
|
||||
2410 F = F + 2 / 3 * P
|
||||
2420 PRINT "AMMUNITION" ;
|
||||
2430 GOSUB 2330
|
||||
2440 LET B = INT ( B + 2 / 3 * P * 50 )
|
||||
2450 PRINT "CLOTHING" ;
|
||||
2460 GOSUB 2330
|
||||
2470 C = C + 2 / 3 * P
|
||||
2480 PRINT "MISCELLANEOUS SUPPLIES" ;
|
||||
2490 GOSUB 2330
|
||||
2500 M1 = M1 + 2 / 3 * P
|
||||
2510 M = M - 45
|
||||
2520 GOTO 2720
|
||||
2530 REM ***HUNTING***
|
||||
2540 IF B > 39 THEN 2570
|
||||
2550 PRINT "TOUGH---YOU NEED MORE BULLETS TO GO HUNTING"
|
||||
2560 GOTO 2080
|
||||
2570 M = M - 45
|
||||
2580 GOSUB 6140
|
||||
2590 IF B1 <= 1 THEN 2660
|
||||
2600 IF 100 + RND ( 1 ) < 13 * B1 THEN 2710
|
||||
2610 F = F + 48 - 2 * B1
|
||||
2620 PRINT "NICE SHOT--RIGHT ON TARGET--GOOD EATIN' TONIGHT!!"
|
||||
2630 B = B - 10 - 3 * B1
|
||||
2640 GOTO 2720
|
||||
2650 REM **BELLS IN LINE 2660**
|
||||
2660 PRINT "RIGHT BETWEEN THE EYES---YOU GOT A BIG ONE!!!!"
|
||||
2670 PRINT "FULL BELLIES TONIGHT!"
|
||||
2680 F = F + 52 + RND ( 1 ) * 6
|
||||
2690 B = B - 10 - RND ( 1 ) * 4
|
||||
2700 GOTO 2720
|
||||
2710 PRINT "YOU MISSED---AND YOUR DINNER GOT AWAY....."
|
||||
2720 IF F >= 13 THEN 2750
|
||||
2730 GOTO 5060
|
||||
2740 REM ***EATING***
|
||||
2750 PRINT "DO YOU WANT TO EAT (1) POORLY (2) MODERATELY"
|
||||
2760 PRINT "OR (3) WELL" ;
|
||||
2770 INPUT E
|
||||
2780 IF E > 3 THEN 2750
|
||||
2790 IF E < 1 THEN 2750
|
||||
2800 LET E = INT ( E )
|
||||
2810 LET F = F - 8 - 5 * E
|
||||
2820 IF F >= 0 THEN 2860
|
||||
2830 F = F + 8 + 5 * E
|
||||
2840 PRINT "YOU CAN'T EAT THAT WELL"
|
||||
2850 GOTO 2750
|
||||
2860 LET M = M + 200 + ( A - 220 ) / 5 + 10 * RND ( 1 )
|
||||
2870 REM LINES 2870-2872 MODIFIED BY C.D.P. FOR COMPATIBILITY W/ CHIPMUNK BASIC
|
||||
2871 C1 = 0
|
||||
2872 L1 = C1
|
||||
2880 REM ***RIDERS ATTACK***
|
||||
2889 REM LINE 2890 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH CHIPMUNK BASIC
|
||||
2900 PRINT "RIDERS AHEAD. THEY " ;
|
||||
2910 S5 = 0
|
||||
2919 REM ALL RND(-1) FUNCTION CALLS HAVE BEEN CHANGED TO RND(1) BY C.D.P.
|
||||
2930 PRINT "DON'T " ;
|
||||
2940 S5 = 1
|
||||
2950 PRINT "LOOK HOSTILE"
|
||||
2960 PRINT "TACTICS"
|
||||
2970 PRINT "(1) RUN (2) ATTACK (3) CONTINUE (4) CIRCLE WAGONS"
|
||||
2990 S5 = 1 - S5
|
||||
3000 INPUT T1
|
||||
3010 IF T1 < 1 THEN 2970
|
||||
3020 IF T1 > 4 THEN 2970
|
||||
3030 T1 = INT ( T1 )
|
||||
3040 IF S5 = 1 THEN 3330
|
||||
3050 IF T1 > 1 THEN 3110
|
||||
3060 M = M + 20
|
||||
3070 M1 = M1 - 15
|
||||
3080 B = B - 150
|
||||
3090 A = A - 40
|
||||
3100 GOTO 3470
|
||||
3110 IF T1 > 2 THEN 3240
|
||||
3120 GOSUB 6140
|
||||
3130 B = B - B1 * 40 - 80
|
||||
3140 IF B1 > 1 THEN 3170
|
||||
3150 PRINT "NICE SHOOTING---YOU DROVE THEM OFF"
|
||||
3160 GOTO 3470
|
||||
3170 IF B1 <= 4 THEN 3220
|
||||
3180 PRINT "LOUSY SHOT---YOU GOT KNIFED"
|
||||
3190 K8 = 1
|
||||
3200 PRINT "YOU HAVE TO SEE OL' DOC BLANCHARD"
|
||||
3210 GOTO 3470
|
||||
3220 PRINT "KINDA SLOW WITH YOUR COLT .45"
|
||||
3230 GOTO 3470
|
||||
3240 IF T1 > 3 THEN 3290
|
||||
3260 LET B = B - 150
|
||||
3270 M1 = M1 - 15
|
||||
3280 GOTO 3470
|
||||
3290 GOSUB 6140
|
||||
3300 B = B - B1 * 30 - 80
|
||||
3310 M = M - 25
|
||||
3320 GOTO 3140
|
||||
3330 IF T1 > 1 THEN 3370
|
||||
3340 M = M + 15
|
||||
3350 A = A - 10
|
||||
3360 GOTO 3470
|
||||
3370 IF T1 > 2 THEN 3410
|
||||
3380 M = M - 5
|
||||
3390 B = B - 100
|
||||
3400 GOTO 3470
|
||||
3410 IF T1 > 3 THEN 3430
|
||||
3420 GOTO 3470
|
||||
3430 M = M - 20
|
||||
3440 GOTO 3470
|
||||
3450 PRINT "THEY DID NOT ATTACK"
|
||||
3460 GOTO 3550
|
||||
3470 IF S5 = 0 THEN 3500
|
||||
3480 PRINT "RIDERS WERE FRIENDLY, BUT CHECK FOR POSSIBLE LOSSES"
|
||||
3490 GOTO 3550
|
||||
3500 PRINT "RIDERS WERE HOSTILE--CHECK FOR LOSES"
|
||||
3510 IF B >= 0 THEN 3550
|
||||
3520 PRINT "YOU RAN OUT OF BULLETS AND GOT MASSACRED BY THE RIDERS"
|
||||
3530 GOTO 5170
|
||||
3540 REM ***SELECTION OF EVENTS***
|
||||
3550 LET D1 = 0
|
||||
3560 RESTORE 3620
|
||||
3570 R1 = 100 * RND ( 1 )
|
||||
3580 LET D1 = D1 + 1
|
||||
3590 IF D1 = 16 THEN 4670
|
||||
3600 READ D
|
||||
3610 IF R1 > D THEN 3580
|
||||
3620 DATA 6 , 11 , 13 , 15 , 17 , 22 , 32 , 35 , 37 , 42 , 44 , 54 , 64 , 69 , 95
|
||||
3630 IF D1 > 10 THEN 3650
|
||||
3640 ON D1 GOTO 3660 , 3700 , 3740 , 3790 , 3820 , 3850 , 3880 , 3960 , 4130 , 4190
|
||||
3650 ON D1 - 10 GOTO 4220 , 4290 , 4340 , 4560 , 4610 , 4670
|
||||
3660 PRINT "WAGON BREAKS DOWN--LOSE TIME AND SUPPLIES FIXING IT"
|
||||
3670 LET M = M - 15 - 5 * RND ( 1 )
|
||||
3680 LET M1 = M1 - 8
|
||||
3690 GOTO 4710
|
||||
3700 PRINT "0X INJURES LEG---SLOWS YOU DOWN REST OF TRIP"
|
||||
3710 LET M = M - 25
|
||||
3720 LET A = A - 20
|
||||
3730 GOTO 4710
|
||||
3740 PRINT "BACK LUCK---YOUR DAUGHTER BROKE HER ARM"
|
||||
3750 PRINT "YOU HAD TO STOP AND USE SUPPLIES TO MAKE A SLING"
|
||||
3760 M = M - 5 - 4 * RND ( 1 )
|
||||
3770 M1 = M1 - 2 - 3 * RND ( 1 )
|
||||
3780 GOTO 4710
|
||||
3790 PRINT "OX WANDERS OFF---SPEND TIME LOOKING FOR IT"
|
||||
3800 M = M - 17
|
||||
3810 GOTO 4710
|
||||
3820 PRINT "YOUR SON GETS LOST---SPEND HALF THE DAY LOOKING FOR HIM"
|
||||
3830 M = M - 10
|
||||
3840 GOTO 4710
|
||||
3850 PRINT "UNSAFE WATER--LOSE TIME LOOKING FOR CLEAN SPRING"
|
||||
3860 LET M = M - 10 * RND ( 1 ) - 2
|
||||
3870 GOTO 4710
|
||||
3880 IF M > 950 THEN 4490
|
||||
3890 PRINT "HEAVY RAINS---TIME AND SUPPLIES LOST"
|
||||
3910 F = F - 10
|
||||
3920 B = B - 500
|
||||
3930 M1 = M1 - 15
|
||||
3940 M = M - 10 * RND ( 1 ) - 5
|
||||
3950 GOTO 4710
|
||||
3960 PRINT "BANDITS ATTACK"
|
||||
3970 GOSUB 6140
|
||||
3980 B = B - 20 * B1
|
||||
3990 IF B >= 0 THEN 4030
|
||||
4000 PRINT "YOU RAN OUT OF BULLETS---THEY GET LOTS OF CASH"
|
||||
4010 T = T / 3
|
||||
4020 GOTO 4040
|
||||
4030 IF B1 <= 1 THEN 4100
|
||||
4040 PRINT "YOU GOT SHOT IN THE LEG AND THEY TOOK ONE OF YOUR OXEN"
|
||||
4050 K8 = 1
|
||||
4060 PRINT "BETTER HAVE A DOC LOOK AT YOUR WOUND"
|
||||
4070 M1 = M1 - 5
|
||||
4080 A = A - 20
|
||||
4090 GOTO 4710
|
||||
4100 PRINT "QUICKEST DRAW OUTSIDE OF DODGE CITY!!!"
|
||||
4110 PRINT "YOU GOT 'EM!"
|
||||
4120 GOTO 4710
|
||||
4130 PRINT "THERE WAS A FIRE IN YOUR WAGON--FOOD AND SUPPLIES DAMAGE!"
|
||||
4140 F = F - 40
|
||||
4150 B = B - 400
|
||||
4160 LET M1 = M1 - RND ( 1 ) * 8 - 3
|
||||
4170 M = M - 15
|
||||
4180 GOTO 4710
|
||||
4190 PRINT "LOSE YOUR WAY IN HEAVY FOG---TIME IS LOST"
|
||||
4200 M = M - 10 - 5 * RND ( 1 )
|
||||
4210 GOTO 4710
|
||||
4220 PRINT "YOU KILLED A POISONOUS SNAKE AFTER IT BIT YOU"
|
||||
4230 B = B - 10
|
||||
4240 M1 = M1 - 5
|
||||
4250 IF M1 >= 0 THEN 4280
|
||||
4260 PRINT "YOU DIE OF SNAKEBITE SINCE YOU HAVE NO MEDICINE"
|
||||
4270 GOTO 5170
|
||||
4280 GOTO 4710
|
||||
4290 PRINT "WAGON GETS SWAMPED FORDING RIVER--LOSE FOOD AND CLOTHES"
|
||||
4300 F = F - 30
|
||||
4310 C = C - 20
|
||||
4320 M = M - 20 - 20 * RND ( 1 )
|
||||
4330 GOTO 4710
|
||||
4340 PRINT "WILD ANIMALS ATTACK!"
|
||||
4350 GOSUB 6140
|
||||
4360 IF B > 39 THEN 4410
|
||||
4370 PRINT "YOU WERE TOO LOW ON BULLETS--"
|
||||
4380 PRINT "THE WOLVES OVERPOWERED YOU"
|
||||
4390 K8 = 1
|
||||
4400 GOTO 5120
|
||||
4410 IF B1 > 2 THEN 4440
|
||||
4420 PRINT "NICE SHOOTIN' PARTNER---THEY DIDN'T GET MUCH"
|
||||
4430 GOTO 4450
|
||||
4440 PRINT "SLOW ON THE DRAW---THEY GOT AT YOUR FOOD AND CLOTHES"
|
||||
4450 B = B - 20 * B1
|
||||
4460 C = C - B1 * 4
|
||||
4470 F = F - B1 * 8
|
||||
4480 GOTO 4710
|
||||
4490 PRINT "COLD WEATHER---BRRRRRRR!---YOU " ;
|
||||
4500 IF C > 22 + 4 * RND ( 1 ) THEN 4530
|
||||
4510 PRINT "DON'T " ;
|
||||
4520 C1 = 1
|
||||
4530 PRINT "HAVE ENOUGH CLOTHING TO KEEP YOU WARM"
|
||||
4540 IF C1 = 0 THEN 4710
|
||||
4550 GOTO 6300
|
||||
4560 PRINT "HAIL STORM---SUPPLIES DAMAGED"
|
||||
4570 M = M - 5 - RND ( 1 ) * 10
|
||||
4580 B = B - 200
|
||||
4590 M1 = M1 - 4 - RND ( 1 ) * 3
|
||||
4600 GOTO 4710
|
||||
4610 IF E = 1 THEN 6300
|
||||
4620 IF E = 3 THEN 4650
|
||||
4630 IF RND(1) > 0.25 THEN 6300
|
||||
4640 GOTO 4710
|
||||
4650 IF RND(1) < 0.5 THEN 6300
|
||||
4660 GOTO 4720
|
||||
4670 PRINT "HELPFUL INDIANS SHOW YOU WHERE TO FIND MORE FOOD"
|
||||
4680 F = F + 14
|
||||
4690 GOTO 4710
|
||||
4700 REM ***MOUNTAINS***
|
||||
4710 IF M <= 950 THEN 1230
|
||||
4719 REM LINE 4720 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH CHIPMUNK BASIC
|
||||
4720 IF RND(1)*10 > 9-(POW((M/100-15),2)+72)/(POW((M/100-15),2)+12) THEN 4860
|
||||
4730 PRINT "RUGGED MOUNTAINS"
|
||||
4740 IF RND(1) > 0.1 THEN 4780
|
||||
4750 PRINT "YOU GOT LOST---LOSE VALUABLE TIME TRYING TO FIND TRAIL!"
|
||||
4760 M = M - 60
|
||||
4770 GOTO 4860
|
||||
4780 IF RND(1) > 0.11 THEN 4840
|
||||
4790 PRINT "WAGON DAMAGED!---LOSE TIME AND SUPPLIES"
|
||||
4800 M1 = M1 - 5
|
||||
4810 B = B - 200
|
||||
4820 M = M - 20 - 30 * RND ( 1 )
|
||||
4830 GOTO 4860
|
||||
4840 PRINT "THE GOING GETS SLOW"
|
||||
4850 M = M - 45 - RND(1) / 0.02
|
||||
4860 IF F1 = 1 THEN 4900
|
||||
4870 F1 = 1
|
||||
4880 IF RND(1) < 0.8 THEN 4970
|
||||
4890 PRINT "YOU MADE IT SAFELY THROUGH SOUTH PASS--NO SNOW"
|
||||
4900 IF M < 1700 THEN 4940
|
||||
4910 IF F2 = 1 THEN 4940
|
||||
4920 F2 = 1
|
||||
4930 IF RND(1) < 0.7 THEN 4970
|
||||
4940 IF M > 950 THEN 1230
|
||||
4950 M9 = 1
|
||||
4960 GOTO 1230
|
||||
4970 PRINT "BLIZZARD IN MOUNTAIN PASS--TIME AND SUPPLIES LOST"
|
||||
4980 L1 = 1
|
||||
4990 F = F - 25
|
||||
5000 M1 = M1 - 10
|
||||
5010 B = B - 300
|
||||
5020 M = M - 30 - 40 * RND ( 1 )
|
||||
5030 IF C < 18 + 2 * RND ( 1 ) THEN 6300
|
||||
5040 GOTO 4940
|
||||
5050 REM ***DYING***
|
||||
5060 PRINT "YOU RAN OUT OF FOOD AND STARVED TO DEATH"
|
||||
5070 GOTO 5170
|
||||
5080 LET T = 0
|
||||
5090 PRINT "YOU CAN'T AFFORD A DOCTOR"
|
||||
5100 GOTO 5120
|
||||
5110 PRINT "YOU RAN OUT OF MEDICAL SUPPLIES"
|
||||
5120 PRINT "YOU DIED OF " ;
|
||||
5130 IF K8 = 1 THEN 5160
|
||||
5140 PRINT "PNEUMONIA"
|
||||
5150 GOTO 5170
|
||||
5160 PRINT "INJURIES"
|
||||
5170 PRINT
|
||||
5180 PRINT "DUE TO YOUR UNFORTUNATE SITUATION, THERE ARE A FEW"
|
||||
5190 PRINT "FORMALITIES WE MUST GO THROUGH"
|
||||
5200 PRINT
|
||||
5210 PRINT "WOULD YOU LIKE A MINISTER?"
|
||||
5220 INPUT C$
|
||||
5230 PRINT "WOULD YOU LIKE A FANCY FUNERAL?"
|
||||
5240 INPUT C$
|
||||
5250 PRINT "WOULD YOU LIKE US TO INFORM YOUR NEXT OF KIN?"
|
||||
5260 INPUT C$
|
||||
5270 IF C$ = "YES" THEN 5310
|
||||
5280 PRINT "BUT YOUR AUNT SADIE IN ST. LOUIS IS REALLY WORRIED ABOUT YOU"
|
||||
5290 PRINT
|
||||
5300 GOTO 5330
|
||||
5310 PRINT "THAT WILL BE $4.50 FOR THE TELEGRAPH CHARGE."
|
||||
5320 PRINT
|
||||
5330 PRINT "WE THANK YOU FOR THIS INFORMATION AND WE ARE SORRY YOU"
|
||||
5340 PRINT "DIDN'T MAKE IT TO THE GREAT TERRITORY OF OREGON"
|
||||
5350 PRINT "BETTER LUCK NEXT TIME"
|
||||
5360 PRINT
|
||||
5370 PRINT
|
||||
5380 PRINT TAB ( 30 ) ; "SINCERELY"
|
||||
5390 PRINT
|
||||
5400 PRINT TAB ( 17 ) ; "THE OREGON CITY CHAMBER OF COMMERCE"
|
||||
5409 REM 'STOP' COMMAND BELOW CHANGED TO 'END' BY C.D.P.
|
||||
5410 END
|
||||
5420 REM ***FINAL TURN***
|
||||
5430 F9 = ( 2040 - M2 ) / ( M - M2 )
|
||||
5440 F = F + ( 1 - F9 ) * ( 8 + 5 * E )
|
||||
5450 PRINT
|
||||
5460 REM **BELLS IN LINES 5470,5480**
|
||||
5470 PRINT "YOU FINALLY ARRIVED AT OREGON CITY"
|
||||
5480 PRINT "AFTER 2040 LONG MILES---HOORAY!!!!!"
|
||||
5490 PRINT "A REAL PIONEER!"
|
||||
5500 PRINT
|
||||
5510 F9 = INT ( F9 * 14 )
|
||||
5520 D3 = D3 * 14 + F9
|
||||
5530 F9 = F9 + 1
|
||||
5540 IF F9 < 8 THEN 5560
|
||||
5550 F9 = F9 - 7
|
||||
5560 ON F9 GOTO 5570 , 5590 , 5610 , 5630 , 5650 , 5670 , 5690
|
||||
5570 PRINT "MONDAY " ;
|
||||
5580 GOTO 5700
|
||||
5590 PRINT "TUESDAY " ;
|
||||
5600 GOTO 5700
|
||||
5610 PRINT "WEDNESDAY " ;
|
||||
5620 GOTO 5700
|
||||
5630 PRINT "THURSDAY " ;
|
||||
5640 GOTO 5700
|
||||
5650 PRINT "FRIDAY " ;
|
||||
5660 GOTO 5700
|
||||
5670 PRINT "SATURDAY " ;
|
||||
5680 GOTO 5700
|
||||
5690 PRINT "SUNDAY " ;
|
||||
5700 IF D3 > 124 THEN 5740
|
||||
5710 D3 = D3 - 93
|
||||
5720 PRINT "JULY " ; D3 ; " 1847"
|
||||
5730 GOTO 5920
|
||||
5740 IF D3 > 155 THEN 5780
|
||||
5750 D3 = D3 - 124
|
||||
5760 PRINT "AUGUST " ; D3 ; " 1847"
|
||||
5770 GOTO 5920
|
||||
5780 IF D3 > 185 THEN 5820
|
||||
5790 D3 = D3 - 155
|
||||
5800 PRINT "SEPTEMBER " ; D3 ; " 1847"
|
||||
5810 GOTO 5920
|
||||
5820 IF D3 > 216 THEN 5860
|
||||
5830 D3 = D3 - 185
|
||||
5840 PRINT "OCTOBER " ; D3 ; " 1847"
|
||||
5850 GOTO 5920
|
||||
5860 IF D3 > 246 THEN 5900
|
||||
5870 D3 = D3 - 216
|
||||
5880 PRINT "NOVEMBER " ; D3 ; " 1847"
|
||||
5890 GOTO 5920
|
||||
5900 D3 = D3 - 246
|
||||
5910 PRINT "DECEMBER " ; D3 ; " 1847"
|
||||
5920 PRINT
|
||||
5930 REM LINE 5930 REMOVED BY C.D.P. FOR COMPATIBILITY WITH MODERN BASIC
|
||||
5940 IF B > 0 THEN 5960
|
||||
5950 LET B = 0
|
||||
5960 IF C > 0 THEN 5980
|
||||
5970 LET C = 0
|
||||
5980 IF M1 > 0 THEN 6000
|
||||
5990 LET M1 = 0
|
||||
6000 IF T > 0 THEN 6020
|
||||
6010 LET T = 0
|
||||
6020 IF F > 0 THEN 6040
|
||||
6030 LET F = 0
|
||||
6040 REM LINES 6040-6045 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH MODERN BASIC
|
||||
6041 PRINT "FOOD: " ; F
|
||||
6042 PRINT "BULLETS: " ; B
|
||||
6043 PRINT "CLOTHING: " ; C
|
||||
6044 PRINT "MISC. SUPP.: " ; M1
|
||||
6045 PRINT "CASH: " ; T
|
||||
6050 PRINT
|
||||
6060 PRINT TAB ( 11 ) ; "PRESIDENT JAMES K. POLK SENDS YOU HIS"
|
||||
6070 PRINT TAB ( 17 ) ; "HEARTIEST CONGRATULATIONS"
|
||||
6080 PRINT
|
||||
6090 PRINT TAB ( 11 ) ; "AND WISHES YOU A PROSPEROUS LIFE AHEAD"
|
||||
6100 PRINT
|
||||
6110 PRINT TAB ( 22 ) ; "AT YOUR NEW HOME"
|
||||
6119 REM 'STOP' COMMAND BELOW CHANGED TO 'END' BY C.D.P.
|
||||
6120 END
|
||||
6130 REM ***SHOOTING SUB-ROUTINE***
|
||||
6131 REM THE METHOD OF TIMING THE SHOOTING (LINES 6210-6240)
|
||||
6132 REM WILL VARY FROM SYSTEM TO SYSTEM. FOR EXAMPLE, H-P
|
||||
6133 REM USERS WILL PROBABLY PREFER TO USE THE 'ENTER' STATEMENT.
|
||||
6134 REM IF TIMING ON THE USER'S SYSTEM IS HIGHLY SUSCEPTIBLE
|
||||
6135 REM TO SYSTEM RESPONSE TIME, THE FORMULA IN LINE 6240 CAN
|
||||
6136 REM BE TAILORED TO ACCOMMODATE THIS BY EITHER INCREASING
|
||||
6137 REM OR DECREASING THE 'SHOOTING' TIME RECORDED BY THE SYSTEM
|
||||
6140 DIM S$ ( 5 )
|
||||
6150 S$ ( 1 ) = "BANG"
|
||||
6160 S$ ( 2 ) = "BLAM"
|
||||
6170 S$ ( 3 ) = "POW"
|
||||
6180 S$ ( 4 ) = "WHAM"
|
||||
6190 S6 = INT ( RND ( 1 ) * 4 + 1 )
|
||||
6200 PRINT "TYPE " ; S$ ( S6 )
|
||||
6201 REM NO TIMER FUNCTION IN PYBASIC, SO RANDOM ELEMENT INTRODUCED
|
||||
6202 REM I'M AWARE THAT THIS CHANGES THE ELEMENT OF 'SKILL' TO
|
||||
6203 REM ONE OF LUCK FOR HUNTING
|
||||
6206 REM 'CLK(0)' FUNCTION CHANGED TO 'TIMER' BY C.D.P. FOR COMPATIBILITY
|
||||
6207 REM B3 = TIMER changed to B3 = TIMER + 2 BY C.D.P. TO MAKE HUNTING...
|
||||
6208 REM ...EASIER ON MODERN COMPUTERS. SEE NOTES FROM ORIGINAL AUTHOR ON
|
||||
6209 REM ... ON TWEAKING HUNTING DIFFICULTY
|
||||
6210 REM B3 = TIMER + 2
|
||||
6220 INPUT C$
|
||||
6229 REM 'CLK(0)' FUNCTION CHANGED TO 'TIMER' BY C.D.P. FOR COMPATIBILITY
|
||||
6230 REM B1 = TIMER
|
||||
6240 REM B1 = ( ( B1 - B3 ) * 3600 ) - ( D9 - 1 )
|
||||
6244 REM IT'S A PYBASIC BODGE, BUT GENERATE RANDOM B1
|
||||
6245 B1 = INT(RND(1)*9)+1
|
||||
6250 PRINT
|
||||
6255 IF B1 > 0 THEN 6260
|
||||
6257 B1 = 0
|
||||
6260 IF C$ = S$ ( S6 ) THEN 6280
|
||||
6270 B1 = 9
|
||||
6280 RETURN
|
||||
6290 REM ***ILLNESS SUB-ROUTINE***
|
||||
6300 IF 100 * RND ( 1 ) < 10 + 35 * ( E - 1 ) THEN 6370
|
||||
6309 REM LINE 6310 MODIFIED BY C.D.P. FOR COMPATIBILITY WITH CHIPMUNK BASIC
|
||||
6310 IF 100*RND(1)<100-POW(40/4,E-1) THEN 6410
|
||||
6320 PRINT "SERIOUS ILLNESS---"
|
||||
6330 PRINT "YOU MUST STOP FOR MEDICAL ATTENTION"
|
||||
6340 M1 = M1 - 10
|
||||
6350 S4 = 1
|
||||
6360 GOTO 6440
|
||||
6370 PRINT "WILD ILLNESS---MEDICINE USED"
|
||||
6380 M = M - 5
|
||||
6390 M1 = M1 - 2
|
||||
6400 GOTO 6440
|
||||
6410 PRINT "BAD ILLNESS---MEDICINE USED"
|
||||
6420 M = M - 5
|
||||
6430 M1 = M1 - 5
|
||||
6440 IF M1 < 0 THEN 5110
|
||||
6450 IF L1 = 1 THEN 4940
|
||||
6460 GOTO 4710
|
||||
6470 REM ***IDENTIFICATION OF VARIABLES IN THE PROGRAM***
|
||||
6480 REM A = AMOUNT SPENT ON ANIMALS
|
||||
6490 REM B = AMOUNT SPENT ON AMMUNITION
|
||||
6500 REM B1 = ACTUAL RESPONSE TIME FOR INPUTTING "BANG"
|
||||
6510 REM B3 = CLOCK TIME AT START OF INPUTTING "BANG"
|
||||
6520 REM C = AMOUNT SPENT ON CLOTHING
|
||||
6530 REM C1 = FLAG FOR INSUFFICIENT CLOTHING IN COLD WEATHER
|
||||
6540 REM C$ = YES/N0 RESPONSE TO QUESTIONS
|
||||
6550 REM D1 = COUNTER IN GENERATING EVENTS
|
||||
6560 REM D3 = TURN NUMBER FOR SETTING DATE
|
||||
6570 REM D4 = CURRENT DATE
|
||||
6580 REM D9 = CHOICE OF SHOOTING EXPERTISE LEVEL
|
||||
6590 REM E = CHOICE OF EATING
|
||||
6600 REM F = AMOUNT SPENT ON FOOD
|
||||
6610 REM F1 = FLAG FOR CLEARING SOUTH PASS
|
||||
6620 REM F2 = FLAG FOR CLEARING BLUE MOUNTAINS
|
||||
6630 REM F9 = FRACTION OF 2 WEEKS TRAVELED ON FINAL TURN
|
||||
6640 REM K8 = FLAG FOR INJURY
|
||||
6650 REM L1 = FLAG FOR BLIZZARD
|
||||
6660 REM M = TOTAL MILEAGE WHOLE TRIP
|
||||
6670 REM M1 = AMOUNT SPENT ON MISCELLANEOUS SUPPLIES
|
||||
6680 REM M2 = TOTAL MILEAGE UP THROUGH PREVIOUS TURN
|
||||
6690 REM M9 = FLAG FOR CLEARING SOUTH PASS IN SETTING MILEAGE
|
||||
6700 REM P = AMOUNT SPENT ON ITEMS AT FORT
|
||||
6710 REM R1 = RANDOM NUMBER IN CHOOSING EVENTS
|
||||
6720 REM S4 = FLAG FOR ILLNESS
|
||||
6730 REM S5 = ""HOSTILITY OF RIDERS"" FACTOR
|
||||
6740 REM S6 = SHOOTING WORD SELECTOR
|
||||
6750 REM S$ = VARIATIONS OF SHOOTING WORD
|
||||
6760 REM T = CASH LEFT OVER AFTER INITIAL PURCHASES
|
||||
6770 REM T1 = CHOICE OF TACTICS WHEN ATTACKED
|
||||
6780 REM X = CHOICE OF ACTION FOR EACH TURN
|
||||
6790 REM X1 = FLAG FOR FORT OPTION
|
||||
6800 END
|
||||
159
builtin_apps/PyBasic/examples/regression.bas
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
10 REM A BASIC PROGRAM THAT CAN BE USED FOR REGRESSION TESTING
|
||||
20 REM OF ALL INTERPRETER FUNCTIONALITY
|
||||
30 PRINT "*** Testing basic arithmetic functions and multiple statements ***"
|
||||
40 LET I = 100: LET J = 200
|
||||
60 PRINT "Expecting the sum to be 300:"
|
||||
70 PRINT I + J
|
||||
80 PRINT "Expecting the product to be 20000:"
|
||||
90 PRINT I * J
|
||||
100 PRINT "Expecting the sum to be 20100:"
|
||||
110 PRINT 100 + I * J
|
||||
120 PRINT "Expecting sum to be 40000"
|
||||
130 PRINT (100 + I) * J
|
||||
140 IF I > J THEN 150 ELSE 180
|
||||
150 PRINT "Should not print the smaller value of I which is "; I
|
||||
160 PRINT I
|
||||
170 GOTO 180
|
||||
180 PRINT "Should print the larger value of J which is "; J
|
||||
190 PRINT J
|
||||
200 GOTO 220
|
||||
210 PRINT "Should not print this line"
|
||||
220 PRINT "*** Testing subroutine behaviour ***"
|
||||
230 PRINT "Calling subroutine"
|
||||
240 GOSUB 1630
|
||||
250 PRINT "Exited subroutine"
|
||||
260 PRINT "Now testing nested subroutines"
|
||||
270 GOSUB 1660
|
||||
280 PRINT "*** Testing loops ***"
|
||||
290 PRINT "This loop should count to 5 in increments of 1:"
|
||||
300 FOR I = 1 TO 5
|
||||
310 PRINT I
|
||||
320 NEXT I
|
||||
330 PRINT "This loop should count back from 10 to 1 in decrements of 2:"
|
||||
340 FOR I = 10 TO 1 STEP -2
|
||||
350 PRINT I
|
||||
360 NEXT I
|
||||
370 PRINT "These nested loops should print 11, 12, 13, 21, 22, 23:"
|
||||
380 FOR I = 1 TO 2
|
||||
390 FOR J = 1 TO 3
|
||||
400 PRINT I; J
|
||||
410 NEXT J
|
||||
420 NEXT I
|
||||
430 PRINT "*** Testing arrays ***"
|
||||
440 DIM A(3, 3)
|
||||
450 FOR I = 0 TO 2
|
||||
460 FOR J = 0 TO 2
|
||||
470 LET A(I, J) = 5
|
||||
480 NEXT J
|
||||
490 NEXT I
|
||||
500 PRINT "This should print 555"
|
||||
510 PRINT A(0, 0); A(1, 1); A(2, 2)
|
||||
520 PRINT "*** Testing file i/o ***"
|
||||
530 OPEN "REGRESSION.TXT" FOR OUTPUT AS #1
|
||||
540 PRINT #1,"0123456789Hello World!"
|
||||
545 PRINT #1,"This is second line for testing"
|
||||
550 CLOSE #1
|
||||
560 OPEN "REGRESSION.TXT" FOR INPUT AS #2
|
||||
570 PRINT "The next line should say 'Hello World!'"
|
||||
580 FSEEK #2,10
|
||||
590 INPUT #2,A$
|
||||
595 PRINT A$
|
||||
600 CLOSE #2
|
||||
601 OPEN "REGRESSION.TXT" FOR APPEND AS #2
|
||||
602 PRINT "3 text lines starting with 0,T,S and ending with !,g,e should follow:"
|
||||
603 PRINT #2,"Should be a seperate line"
|
||||
604 CLOSE #2
|
||||
605 OPEN "REGRESSION.TXT" FOR INPUT AS #2
|
||||
606 FOR I = 1 TO 2
|
||||
607 INPUT #2,A$:PRINT A$
|
||||
608 NEXT I
|
||||
609 INPUT #2,A$
|
||||
610 FOR I = 1 TO 25
|
||||
611 PRINT MID$(A$,I,1);
|
||||
612 NEXT I
|
||||
613 PRINT:CLOSE #2
|
||||
614 OPEN "NOFILE.X7Z" FOR INPUT AS #2 ELSE 620
|
||||
615 PRINT "***This Message should NOT be Displayed***"
|
||||
620 N = 0
|
||||
630 I = 7
|
||||
640 PRINT "This loop should count to 5 in increments of 1 twice:"
|
||||
650 FOR I = 1 TO 10
|
||||
660 PRINT I
|
||||
670 IF I = 5 THEN GOTO 690
|
||||
680 NEXT I
|
||||
690 N = N + 1
|
||||
700 IF N < 2 THEN GOTO 650
|
||||
810 PRINT "The loop variable I should be equal to 5, I=";I
|
||||
815 DATA "DATA Statement tests..."
|
||||
820 READ A$
|
||||
825 PRINT "The next line should read: DATA Statement tests..."
|
||||
830 PRINT A$
|
||||
840 DATA 1 , 2 , 3
|
||||
850 DATA 4 , 5 , 6
|
||||
855 DATA 1.5 , 2 , "test"
|
||||
858 PRINT "The next three lines should be: 12 34 56"
|
||||
860 FOR I = 1 TO 3
|
||||
870 READ J , K
|
||||
880 PRINT J ; K
|
||||
890 NEXT I
|
||||
900 RESTORE 840
|
||||
910 READ I , J , K
|
||||
920 PRINT "the next line should print 123"
|
||||
930 PRINT I ; J ; K
|
||||
970 RESTORE 855
|
||||
980 READ A1 , B , C$
|
||||
990 PRINT "Float: " ; A1 ; " Int: " ; B ; " String: " ; C$
|
||||
1000 RESTORE 850
|
||||
1010 READ I , J , K , L
|
||||
1020 PRINT "the next line should print 4561.5:"
|
||||
1030 PRINT I; J ; K ; L
|
||||
1040 PRINT "The next lines should print: 'Hello World' and then 'AGAIN' under the word 'World'"
|
||||
1050 PRINT "Hel" ; : PRINT "lo" ; TAB ( 7 ) ; "World" ; TAB ( 7 ) ; "AGAIN"
|
||||
1060 PRINT "This loop should count from 1 to 10"
|
||||
1070 FOR I = 1 TO 10
|
||||
1080 FOR J = 1 TO 3
|
||||
1090 PRINT I
|
||||
1100 GOTO 1130
|
||||
1120 NEXT J
|
||||
1130 NEXT I
|
||||
1140 INPUT "Enter T for the THEN blocks to execute, E for the ELSE (T/E): " ; ANS$
|
||||
1150 ANS$ = UPPER$ ( ANS$ )
|
||||
1160 IF ANS$ = "T" THEN 1190
|
||||
1170 PRINT "1170: Did not enter T"
|
||||
1180 GOTO 1200
|
||||
1190 PRINT "1190: Entered T"
|
||||
1200 IF ANS$ = "T" THEN GOTO 1230
|
||||
1210 PRINT "1210: Did not enter T"
|
||||
1220 GOTO 1240
|
||||
1230 PRINT "1230: Entered T"
|
||||
1240 IF ANS$ = "T" THEN 1280 ELSE 1260
|
||||
1250 PRINT "ERROR - Should not be at line 1250"
|
||||
1260 PRINT "1260: Did not enter T"
|
||||
1270 GOTO 1290
|
||||
1280 PRINT "1280: Entered T"
|
||||
1290 IF ANS$ = "T" THEN GOTO 1330 ELSE GOTO 1310
|
||||
1300 PRINT "ERROR - Should not be at line 1300"
|
||||
1310 PRINT "1310: Did not enter T"
|
||||
1320 GOTO 1340
|
||||
1330 PRINT "1330: Entered T"
|
||||
1340 IF ANS$ = "T" THEN PRINT "1340: Entered T"
|
||||
1350 IF ANS$ <> "T" THEN PRINT "1350: Did not enter T"
|
||||
1360 IF ANS$ = "T" THEN PRINT "1360: Entered T" ELSE PRINT "1360: Did not enter T"
|
||||
1370 IF ANS$ = "T" THEN PRINT "1370: Entered T" : GOTO 1390
|
||||
1380 IF ANS$ <> "T" THEN PRINT "1380: Did not enter T"
|
||||
1390 IF ANS$ = "T" THEN PRINT "1390: Entered " ; : PRINT "T" ELSE PRINT "1390: Did " ; : PRINT "not enter T"
|
||||
1400 IF ANS$ = "T" THEN PRINT "1400: Entered T" ELSE PRINT "1400: Did " ; : PRINT "not enter T"
|
||||
1410 IF ANS$ = "T" THEN PRINT "1410: Entered " ; : PRINT "T" ELSE PRINT "1410: Did not enter T"
|
||||
1420 PRINT "Compound Stmt w/conditionals ";:IF ANS$ = "T" THEN PRINT "1420: Entered " ; : PRINT "T" ELSE PRINT "1420: Did " ; : PRINT "not enter T"
|
||||
1610 PRINT "*** Finished ***"
|
||||
1620 STOP
|
||||
1630 REM A SUBROUTINE TEST
|
||||
1640 PRINT "Executing the subroutine"
|
||||
1650 RETURN
|
||||
1660 REM AN OUTER SUBROUTINE
|
||||
1670 GOSUB 1700
|
||||
1680 PRINT "This should be printed second"
|
||||
1690 RETURN
|
||||
1700 REM A NESTED SUBROUTINE
|
||||
1710 PRINT "This should be printed first"
|
||||
1720 RETURN
|
||||
63
builtin_apps/PyBasic/examples/rock_scissors_paper.bas
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
10 REM ROCK, SCISSORS, PAPER AGAINST
|
||||
20 REM THE COMPUTER
|
||||
30 RANDOMIZE
|
||||
40 MYSCORE = 0
|
||||
50 YOURSCORE = 0
|
||||
60 PRINT "Let's play rock-scissors-paper!"
|
||||
70 INPUT "(R)ock, (S)cissors, (P)aper or e(X)it: "; YOURGUESS$
|
||||
75 IF YOURGUESS$ = "X" THEN 160
|
||||
80 RANDOM = RND(1)
|
||||
90 GOSUB 190
|
||||
100 PRINT "Your guess: "; YOURGUESS$; ", My guess: "; MYGUESS$
|
||||
110 REM ON YOURGUESS$ = "R" GOSUB 300
|
||||
120 REM ON YOURGUESS$ = "S" GOSUB 500
|
||||
130 REM ON YOURGUESS$ = "P" GOSUB 700
|
||||
135 ON INSTR("RSP",YOURGUESS$) GOSUB 300,500,700
|
||||
150 GOTO 70
|
||||
160 PRINT "My score: "; MYSCORE; " Your score: "; YOURSCORE
|
||||
170 STOP
|
||||
190 REM RANDOMLY ASSIGN MYGUESS$
|
||||
200 IF RANDOM < 0.3 THEN 210 ELSE 230
|
||||
210 MYGUESS$ = "R"
|
||||
220 RETURN
|
||||
230 IF RANDOM < 0.6 THEN 240 ELSE 260
|
||||
240 MYGUESS$ = "S"
|
||||
250 RETURN
|
||||
260 MYGUESS$ = "P"
|
||||
270 RETURN
|
||||
300 REM ROCK
|
||||
310 IF MYGUESS$ = "R" THEN 320 ELSE 340
|
||||
320 PRINT "A draw!"
|
||||
330 RETURN
|
||||
340 IF MYGUESS$ = "S" THEN 350 ELSE 380
|
||||
350 PRINT "I lost!"
|
||||
360 YOURSCORE = YOURSCORE + 1
|
||||
370 RETURN
|
||||
380 IF MYGUESS$ = "P" THEN 390 ELSE 410
|
||||
390 PRINT "I won!"
|
||||
400 MYSCORE = MYSCORE + 1
|
||||
410 RETURN
|
||||
500 REM SCISSORS
|
||||
510 IF MYGUESS$ = "S" THEN 520 ELSE 540
|
||||
520 PRINT "A draw!"
|
||||
530 RETURN
|
||||
540 IF MYGUESS$ = "P" THEN 550 ELSE 580
|
||||
550 PRINT "I lost!"
|
||||
560 YOURSCORE = YOURSCORE + 1
|
||||
570 RETURN
|
||||
580 IF MYGUESS$ = "R" THEN 390 ELSE 410
|
||||
590 PRINT "I won!"
|
||||
600 MYSCORE = MYSCORE + 1
|
||||
610 RETURN
|
||||
700 REM PAPER
|
||||
710 IF MYGUESS$ = "P" THEN 720 ELSE 740
|
||||
720 PRINT "A draw!"
|
||||
730 RETURN
|
||||
740 IF MYGUESS$ = "R" THEN 750 ELSE 780
|
||||
750 PRINT "I lost!"
|
||||
760 YOURSCORE = YOURSCORE + 1
|
||||
770 RETURN
|
||||
780 IF MYGUESS$ = "S" THEN 790 ELSE 810
|
||||
790 PRINT "I won!"
|
||||
800 MYSCORE = MYSCORE + 1
|
||||
810 RETURN
|
||||
95
builtin_apps/PyBasic/examples/tlife.bas
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
2 PRINT TAB ( 34 ) ; "LIFE"
|
||||
4 PRINT TAB ( 15 ) ; "CREATIVE COMPUTING MORRISTOWN NEW JERSEY"
|
||||
6 PRINT : PRINT : PRINT
|
||||
8 PRINT "ENTER YOUR PATTERN, AS A SERIES OF SPACES"
|
||||
9 PRINT "AND STARS, LINE BY LINE. FINISH BY TYPING"
|
||||
10 PRINT "'DONE' ON THE FINAL LINE:"
|
||||
11 X1 = 1 : Y1 = 1 : X2 = 24 : Y2 = 70 : G = 0 : P = 0
|
||||
12 DIM A ( 24 , 70 ) : DIM B$ ( 24 )
|
||||
15 I9 = 1
|
||||
20 C = 1
|
||||
30 INPUT TEMP$
|
||||
35 B$ ( C ) = TEMP$
|
||||
40 IF B$ ( C ) = "DONE" THEN 45 ELSE 50
|
||||
45 B$ ( C ) = "" : GOTO 80
|
||||
46 REM ORIGINAL VERSION REQUIRED USER TO WRITE
|
||||
47 REM DOTS INSTEAD OF SPACES, WHICH WOULD THEN BE
|
||||
48 REM CONVERTED TO SPACES
|
||||
50 REM IF LEFT$(B$(C), 1)="." THEN 55 ELSE 60
|
||||
55 REM B$(C)=" "+RIGHT$(B$(C), LEN(B$(C))-1)
|
||||
60 C = C + 1
|
||||
70 GOTO 30
|
||||
80 C = C - 1 : L = 0
|
||||
90 FOR X = 1 TO C - 1
|
||||
100 IF LEN ( B$ ( X ) ) > L THEN 105 ELSE 110
|
||||
105 L = LEN ( B$ ( X ) )
|
||||
110 NEXT X
|
||||
120 X1 = INT ( 11 - C / 2 )
|
||||
130 Y1 = INT ( 33 - L / 2 )
|
||||
135 REM TRANSCRIBE INPUT PATTERN INTO ARRAY
|
||||
140 FOR X = 1 TO C
|
||||
150 FOR Y = 1 TO LEN ( B$ ( X ) )
|
||||
160 IF MID$ ( B$ ( X ) , Y , 1 ) <> " " THEN 165 ELSE 170
|
||||
165 A ( X1 + X , Y1 + Y ) = 1 : P = P + 1
|
||||
170 NEXT Y
|
||||
180 NEXT X
|
||||
200 PRINT CHR$ ( 27 ) + "[2J"
|
||||
210 PRINT CHR$ ( 27 ) + "[2;1HGENERATION: " ; G ; " POPULATION: " ; P ; CHR$ ( 27 ) + "[K" ;
|
||||
212 IF I9 = 0 THEN 213 ELSE 215
|
||||
213 PRINT " INVALID!" ; CHR$ ( 27 ) + "[K" ;
|
||||
215 X3 = 24 : Y3 = 70 : X4 = 1 : Y4 = 1 : P = 0
|
||||
220 G = G + 1
|
||||
230 FOR X = X1 TO X2
|
||||
240 IF X-X1+1 < 17 THEN PRINT CHR$ ( 27 ) + "[K"
|
||||
250 FOR Y = Y1 TO Y2
|
||||
253 IF A ( X , Y ) = 2 THEN 254 ELSE 256
|
||||
254 A ( X , Y ) = 0 : GOTO 270
|
||||
256 IF A ( X , Y ) = 3 THEN 258 ELSE 260
|
||||
258 A ( X , Y ) = 1 : GOTO 261
|
||||
260 IF A ( X , Y ) <> 1 THEN 270
|
||||
261 IF X-X1+1 < 17 THEN PRINT TAB ( Y ) ; "*" ;
|
||||
262 IF X < X3 THEN 263 ELSE 264
|
||||
263 X3 = X
|
||||
264 IF X > X4 THEN 265 ELSE 266
|
||||
265 X4 = X
|
||||
266 IF Y < Y3 THEN 267 ELSE 268
|
||||
267 Y3 = Y
|
||||
268 IF Y > Y4 THEN 269 ELSE 270
|
||||
269 Y4 = Y
|
||||
270 NEXT Y
|
||||
290 NEXT X
|
||||
293 PRINT
|
||||
295 FOR X = X2 + 1 TO 24
|
||||
296 IF X-X1+1 < 17 THEN PRINT " "*71
|
||||
297 NEXT X
|
||||
299 X1 = X3 : X2 = X4 : Y1 = Y3 : Y2 = Y4
|
||||
301 IF X1 < 3 THEN 302 ELSE 303
|
||||
302 X1 = 3 : I9 = - 1
|
||||
303 IF X2 > 22 THEN 304 ELSE 305
|
||||
304 X2 = 22 : I9 = - 1
|
||||
305 IF Y1 < 3 THEN 306 ELSE 307
|
||||
306 Y1 = 3 : I9 = - 1
|
||||
307 IF Y2 > 68 THEN 308 ELSE 309
|
||||
308 Y2 = 68 : I9 = - 1
|
||||
309 P = 0
|
||||
500 FOR X = X1 - 1 TO X2 + 1
|
||||
510 FOR Y = Y1 - 1 TO Y2 + 1
|
||||
520 C = 0
|
||||
530 FOR I = X - 1 TO X + 1
|
||||
540 FOR J = Y - 1 TO Y + 1
|
||||
550 IF A ( I , J ) = 1 OR A ( I , J ) = 2 THEN 555 ELSE 560
|
||||
555 C = C + 1
|
||||
560 NEXT J
|
||||
570 NEXT I
|
||||
580 IF A ( X , Y ) = 0 THEN 610
|
||||
590 IF C < 3 OR C > 4 THEN 592 ELSE 595
|
||||
592 A ( X , Y ) = 2 : GOTO 600
|
||||
595 P = P + 1
|
||||
600 GOTO 620
|
||||
610 IF C = 3 THEN 615 ELSE 620
|
||||
615 A ( X , Y ) = 3 : P = P + 1
|
||||
620 NEXT Y
|
||||
630 NEXT X
|
||||
635 X1 = X1 - 1 : Y1 = Y1 - 1 : X2 = X2 + 1 : Y2 = Y2 + 1
|
||||
640 GOTO 210
|
||||
650 END
|
||||
110
builtin_apps/PyBasic/flowsignal.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""This class defines an object that can be returned by the BASICParser to
|
||||
indicate the need for a control flow branch. The information within the
|
||||
object tells the Program the nature of the jump and therefore whether a
|
||||
return address need to be added to the return stack.
|
||||
|
||||
>>> flowsignal = FlowSignal(ftype=FlowSignal.RETURN)
|
||||
>>> print(flowsignal.ftarget)
|
||||
-1
|
||||
>>> flowsignal = FlowSignal(ftarget=100, ftype=FlowSignal.SIMPLE_JUMP)
|
||||
>>> print(flowsignal.ftarget)
|
||||
100
|
||||
>>> print(flowsignal.ftype)
|
||||
0
|
||||
"""
|
||||
|
||||
|
||||
class FlowSignal:
|
||||
|
||||
# Jump categories
|
||||
|
||||
# Indicates a simple jump as the result
|
||||
# of a GOTO or conditional branch. The
|
||||
# ftarget value should be the jump target, i.e.
|
||||
# the line number being jumped to
|
||||
SIMPLE_JUMP = 0
|
||||
|
||||
# Indicates a subroutine call where the
|
||||
# return address must be the line number of the instruction
|
||||
# of the following the call.
|
||||
# The ftarget value should be the line number of the first line
|
||||
# of the subroutine
|
||||
GOSUB = 1
|
||||
|
||||
# Indicates the start of a FOR loop where loop
|
||||
# variable has not reached the end value, and therefore the loop
|
||||
# must be repeated. There should be therefore be
|
||||
# no ftarget value associated with it
|
||||
LOOP_BEGIN = 2
|
||||
|
||||
# An indication from a processed NEXT statement that the loop is to
|
||||
# be repeated. Since the return address is already on the stack,
|
||||
# there does not need to be an ftarget value associated with the signal.
|
||||
LOOP_REPEAT = 3
|
||||
|
||||
# An indication from a FOR statement that the loop should be skipped because
|
||||
# loop variable has reached its end value. The ftarget should be
|
||||
# the loop variable to look for in the terminating NEXT statement
|
||||
LOOP_SKIP = 4
|
||||
|
||||
# Indicates a subroutine return has been processed, where the return
|
||||
# address is on the return stack. There should be therefore
|
||||
# be no ftarget value specified
|
||||
RETURN = 5
|
||||
|
||||
# Indicates that execution should cease because a stop statement has
|
||||
# been processed. There should be therefore be no ftarget value specified
|
||||
STOP = 6
|
||||
|
||||
# Indicates that a conditional result block should be executed
|
||||
EXECUTE = 7
|
||||
|
||||
def __init__(self, ftarget=None, ftype=SIMPLE_JUMP, floop_var=None):
|
||||
"""Creates a new FlowSignal for a branch. If the jump
|
||||
target is supplied, then the branch is assumed to be
|
||||
either a GOTO or conditional branch and the type is assigned as
|
||||
SIMPLE_JUMP. If no jump_target is supplied, then a jump_type must be
|
||||
supplied, which must either be GOSUB, RETURN, LOOP_BEGIN,
|
||||
LOOP_REPEAT, LOOP_SKIP or STOP. In the latter cases
|
||||
the jump target is assigned an arbitrary value of None.
|
||||
|
||||
:param ftarget: The associated value
|
||||
:param ftype: Either GOSUB, SIMPLE_JUMP, RETURN, LOOP_BEGIN,
|
||||
LOOP_SKIP or STOP
|
||||
:param floop_var: The loop variable of a FOR/NEXT loop
|
||||
"""
|
||||
|
||||
if ftype not in [self.GOSUB, self.SIMPLE_JUMP, self.LOOP_BEGIN,
|
||||
self.LOOP_REPEAT, self.RETURN,
|
||||
self.LOOP_SKIP, self.STOP, self.EXECUTE]:
|
||||
raise TypeError("Invalid flow signal type supplied: " + str(ftype))
|
||||
|
||||
if ftarget == None and \
|
||||
ftype in [self.SIMPLE_JUMP, self.GOSUB, self.LOOP_SKIP]:
|
||||
raise TypeError("Invalid jump target supplied for flow signal type: " + str(ftarget))
|
||||
|
||||
if ftarget != None and \
|
||||
ftype in [self.RETURN, self.LOOP_BEGIN, self.LOOP_REPEAT,
|
||||
self.STOP, self.EXECUTE]:
|
||||
raise TypeError("Target wrongly supplied for flow signal " + str(ftype))
|
||||
|
||||
self.ftype = ftype
|
||||
self.ftarget = ftarget
|
||||
self.floop_var = floop_var
|
||||
135
builtin_apps/PyBasic/interpreter.py
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""This class implements a BASIC interpreter that
|
||||
presents a prompt to the user. The user may input
|
||||
program statements, list them and run the program.
|
||||
The program may also be saved to disk and loaded
|
||||
again.
|
||||
|
||||
"""
|
||||
|
||||
from basictoken import BASICToken as Token
|
||||
from lexer import Lexer
|
||||
from program import Program
|
||||
from sys import stderr
|
||||
from traceback import print_exception
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
banner = (r"""
|
||||
._____________ ___________. ___ ___________ ______
|
||||
| _ .__ \ / ___ _ \ / \ / | / |
|
||||
| |_) | \ \/ / | |_) | / ^ \ | (----`| | | ,----'
|
||||
| ___/ \_ _/ | _ < / /_\ \ \ \ | | | |
|
||||
| | | | | |_) \/ _____ \----) | | | | `----.
|
||||
| _| |__| |___________/ \_________/ |____________|
|
||||
""")
|
||||
|
||||
print(banner)
|
||||
|
||||
lexer = Lexer()
|
||||
program = Program()
|
||||
|
||||
# Continuously accept user input and act on it until
|
||||
# the user enters 'EXIT'
|
||||
while True:
|
||||
|
||||
stmt = input('> ')
|
||||
|
||||
try:
|
||||
tokenlist = lexer.tokenize(stmt)
|
||||
|
||||
# Execute commands directly, otherwise
|
||||
# add program statements to the stored
|
||||
# BASIC program
|
||||
|
||||
if len(tokenlist) > 0:
|
||||
|
||||
# Exit the interpreter
|
||||
if tokenlist[0].category == Token.EXIT:
|
||||
break
|
||||
|
||||
# Add a new program statement, beginning
|
||||
# a line number
|
||||
elif tokenlist[0].category == Token.UNSIGNEDINT\
|
||||
and len(tokenlist) > 1:
|
||||
program.add_stmt(tokenlist)
|
||||
|
||||
# Delete a statement from the program
|
||||
elif tokenlist[0].category == Token.UNSIGNEDINT \
|
||||
and len(tokenlist) == 1:
|
||||
program.delete_statement(int(tokenlist[0].lexeme))
|
||||
|
||||
# Execute the program
|
||||
elif tokenlist[0].category == Token.RUN:
|
||||
try:
|
||||
program.execute()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("Program terminated")
|
||||
|
||||
# List the program
|
||||
elif tokenlist[0].category == Token.LIST:
|
||||
if len(tokenlist) == 2:
|
||||
program.list(int(tokenlist[1].lexeme),int(tokenlist[1].lexeme))
|
||||
elif len(tokenlist) == 3:
|
||||
# if we have 3 tokens, it might be LIST x y for a range
|
||||
# or LIST -y or list x- for a start to y, or x to end
|
||||
if tokenlist[1].lexeme == "-":
|
||||
program.list(None, int(tokenlist[2].lexeme))
|
||||
elif tokenlist[2].lexeme == "-":
|
||||
program.list(int(tokenlist[1].lexeme), None)
|
||||
else:
|
||||
program.list(int(tokenlist[1].lexeme),int(tokenlist[2].lexeme))
|
||||
elif len(tokenlist) == 4:
|
||||
# if we have 4, assume LIST x-y or some other
|
||||
# delimiter for a range
|
||||
program.list(int(tokenlist[1].lexeme),int(tokenlist[3].lexeme))
|
||||
else:
|
||||
program.list()
|
||||
|
||||
# Save the program to disk
|
||||
elif tokenlist[0].category == Token.SAVE:
|
||||
program.save(tokenlist[1].lexeme)
|
||||
print("Program written to file")
|
||||
|
||||
# Load the program from disk
|
||||
elif tokenlist[0].category == Token.LOAD:
|
||||
program.load(tokenlist[1].lexeme)
|
||||
print("Program read from file")
|
||||
|
||||
# Delete the program from memory
|
||||
elif tokenlist[0].category == Token.NEW:
|
||||
program.delete()
|
||||
|
||||
# Unrecognised input
|
||||
else:
|
||||
print("Unrecognised input", file=stderr)
|
||||
for token in tokenlist:
|
||||
token.print_lexeme()
|
||||
print(flush=True)
|
||||
|
||||
# Trap all exceptions so that interpreter
|
||||
# keeps running
|
||||
except Exception as e:
|
||||
print_exception(e,e, \
|
||||
e.__traceback__ if hasattr(e,'__traceback__') else None)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
202
builtin_apps/PyBasic/lexer.py
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""This class implements a lexical analyser capable
|
||||
of consuming BASIC statements and commands and returning
|
||||
a corresponding list of tokens.
|
||||
|
||||
>>> lexer = Lexer()
|
||||
>>> tokenlist = lexer.tokenize('100 LET I = 10')
|
||||
>>> tokenlist[0].pretty_print()
|
||||
Column: 0 Category: UNSIGNEDINT Lexeme: 100
|
||||
>>> tokenlist = lexer.tokenize('100 IF I <> 10')
|
||||
>>> tokenlist[3].pretty_print()
|
||||
Column: 9 Category: NOTEQUAL Lexeme: <>
|
||||
>>> tokenlist = lexer.tokenize('100 LET I = 3.45')
|
||||
>>> tokenlist[4].pretty_print()
|
||||
Column: 12 Category: UNSIGNEDFLOAT Lexeme: 3.45
|
||||
>>> tokenlist = lexer.tokenize('100 LET I = "HELLO"')
|
||||
>>> tokenlist[4].pretty_print()
|
||||
Column: 12 Category: STRING Lexeme: HELLO
|
||||
"""
|
||||
|
||||
from basictoken import BASICToken as Token
|
||||
|
||||
|
||||
class Lexer:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.__column = 0 # Current column number
|
||||
self.__stmt = '' # Statement string being processed
|
||||
|
||||
def tokenize(self, stmt):
|
||||
"""Returns a list of tokens obtained by
|
||||
lexical analysis of the specified
|
||||
statement.
|
||||
|
||||
"""
|
||||
self.__stmt = stmt
|
||||
self.__column = 0
|
||||
|
||||
# Establish a list of tokens to be
|
||||
# derived from the statement
|
||||
tokenlist = []
|
||||
|
||||
# Process every character until we
|
||||
# reach the end of the statement string
|
||||
c = self.__get_next_char()
|
||||
while c != '':
|
||||
|
||||
# Skip any preceding whitespace
|
||||
while c.isspace():
|
||||
c = self.__get_next_char()
|
||||
|
||||
# Construct a token, column count already
|
||||
# incremented
|
||||
token = Token(self.__column - 1, None, '')
|
||||
|
||||
# Process strings
|
||||
if c == '"':
|
||||
token.category = Token.STRING
|
||||
|
||||
# Consume all of the characters
|
||||
# until we reach the terminating
|
||||
# quote. Do not store the quotes
|
||||
# in the lexeme
|
||||
c = self.__get_next_char() # Advance past opening quote
|
||||
|
||||
# We explicitly support empty strings
|
||||
if c == '"':
|
||||
# String is empty, leave lexeme as ''
|
||||
# and advance past terminating quote
|
||||
c = self.__get_next_char()
|
||||
|
||||
else:
|
||||
while True:
|
||||
token.lexeme += c # Append the current char to the lexeme
|
||||
c = self.__get_next_char()
|
||||
|
||||
if c == '':
|
||||
raise SyntaxError("Mismatched quotes")
|
||||
|
||||
if c == '"':
|
||||
c = self.__get_next_char() # Advance past terminating quote
|
||||
break
|
||||
|
||||
# Process numbers
|
||||
elif c.isdigit() or c == '.':
|
||||
token.category = Token.UNSIGNEDINT
|
||||
found_point = False
|
||||
if c == '.':
|
||||
token.category = Token.UNSIGNEDFLOAT
|
||||
found_point = True
|
||||
|
||||
# Consume all of the digits, including any decimal point
|
||||
while True:
|
||||
token.lexeme += c # Append the current char to the lexeme
|
||||
c = self.__get_next_char()
|
||||
|
||||
# Break if next character is not a digit
|
||||
# and this is not the first decimal point
|
||||
if not c.isdigit():
|
||||
if c == '.':
|
||||
if found_point is False:
|
||||
found_point = True
|
||||
token.category = Token.UNSIGNEDFLOAT
|
||||
|
||||
else:
|
||||
# Another decimal point found
|
||||
break
|
||||
|
||||
else:
|
||||
break
|
||||
|
||||
# Process keywords and names
|
||||
elif c.isalpha():
|
||||
# Consume all of the letters
|
||||
while True:
|
||||
token.lexeme += c # append the current char to the lexeme
|
||||
c = self.__get_next_char()
|
||||
|
||||
# Break if not a letter or a dollar symbol
|
||||
# (the latter is used for string variable names)
|
||||
if not ((c.isalpha() or c.isdigit()) or c == '_' or c == '$'):
|
||||
break
|
||||
|
||||
# Normalise keywords and names to upper case
|
||||
token.lexeme = token.lexeme.upper()
|
||||
|
||||
# Determine if the lexeme is a variable name or a
|
||||
# reserved word
|
||||
if token.lexeme in Token.keywords:
|
||||
token.category = Token.keywords[token.lexeme]
|
||||
|
||||
else:
|
||||
token.category = Token.NAME
|
||||
|
||||
# Remark Statements - process rest of statement without checks
|
||||
if token.lexeme == "REM":
|
||||
while c!= '':
|
||||
token.lexeme += c # Append the current char to the lexeme
|
||||
c = self.__get_next_char()
|
||||
|
||||
# Process operator symbols
|
||||
elif c in Token.smalltokens:
|
||||
save = c
|
||||
c = self.__get_next_char() # c might be '' (end of stmt)
|
||||
twochar = save + c
|
||||
|
||||
if twochar in Token.smalltokens:
|
||||
token.category = Token.smalltokens[twochar]
|
||||
token.lexeme = twochar
|
||||
c = self.__get_next_char() # Move past end of token
|
||||
|
||||
else:
|
||||
# One char token
|
||||
token.category = Token.smalltokens[save]
|
||||
token.lexeme = save
|
||||
|
||||
# We do not recognise this token
|
||||
elif c != '':
|
||||
raise SyntaxError('Syntax error')
|
||||
|
||||
# Append the new token to the list
|
||||
tokenlist.append(token)
|
||||
|
||||
return tokenlist
|
||||
|
||||
def __get_next_char(self):
|
||||
"""Returns the next character in the
|
||||
statement, unless the last character has already
|
||||
been processed, in which case, the empty string is
|
||||
returned.
|
||||
|
||||
"""
|
||||
if self.__column < len(self.__stmt):
|
||||
next_char = self.__stmt[self.__column]
|
||||
self.__column = self.__column + 1
|
||||
|
||||
return next_char
|
||||
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
4
builtin_apps/PyBasic/metadata.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"title": "PyBasic",
|
||||
"icon": "PyBasic.bmp"
|
||||
}
|
||||
487
builtin_apps/PyBasic/program.py
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""Class representing a BASIC program.
|
||||
This is a list of statements, ordered by
|
||||
line number.
|
||||
|
||||
"""
|
||||
|
||||
from basictoken import BASICToken as Token
|
||||
from basicparser import BASICParser
|
||||
from flowsignal import FlowSignal
|
||||
from lexer import Lexer
|
||||
import os
|
||||
|
||||
|
||||
class BASICData:
|
||||
|
||||
def __init__(self):
|
||||
# array of line numbers to represent data statements
|
||||
self.__datastmts = {}
|
||||
|
||||
# Data pointer
|
||||
self.__next_data = 0
|
||||
|
||||
|
||||
def delete(self):
|
||||
self.__datastmts.clear()
|
||||
self.__next_data = 0
|
||||
|
||||
def delData(self,line_number):
|
||||
if self.__datastmts.get(line_number) != None:
|
||||
del self.__datastmts[line_number]
|
||||
|
||||
def addData(self,line_number,tokenlist):
|
||||
"""
|
||||
Adds the supplied token list
|
||||
to the program's DATA store. If a token list with the
|
||||
same line number already exists, this is
|
||||
replaced.
|
||||
|
||||
line_number: Basic program line number of DATA statement
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
self.__datastmts[line_number] = tokenlist
|
||||
|
||||
except TypeError as err:
|
||||
raise TypeError("Invalid line number: " + str(err))
|
||||
|
||||
|
||||
def getTokens(self,line_number):
|
||||
"""
|
||||
returns the tokens from the program DATA statement
|
||||
|
||||
line_number: Basic program line number of DATA statement
|
||||
|
||||
"""
|
||||
|
||||
return self.__datastmts.get(line_number)
|
||||
|
||||
def readData(self,read_line_number):
|
||||
|
||||
if len(self.__datastmts) == 0:
|
||||
raise RuntimeError('No DATA statements available to READ ' +
|
||||
'in line ' + str(read_line_number))
|
||||
|
||||
data_values = []
|
||||
|
||||
line_numbers = list(self.__datastmts.keys())
|
||||
line_numbers.sort()
|
||||
|
||||
if self.__next_data == 0:
|
||||
self.__next_data = line_numbers[0]
|
||||
elif line_numbers.index(self.__next_data) < len(line_numbers)-1:
|
||||
self.__next_data = line_numbers[line_numbers.index(self.__next_data)+1]
|
||||
else:
|
||||
raise RuntimeError('No DATA statements available to READ ' +
|
||||
'in line ' + str(read_line_number))
|
||||
|
||||
tokenlist = self.__datastmts[self.__next_data]
|
||||
|
||||
sign = 1
|
||||
for token in tokenlist[1:]:
|
||||
if token.category != Token.COMMA:
|
||||
#data_values.append(token.lexeme)
|
||||
|
||||
if token.category == Token.STRING:
|
||||
data_values.append(token.lexeme)
|
||||
elif token.category == Token.UNSIGNEDINT:
|
||||
data_values.append(sign*int(token.lexeme))
|
||||
elif token.category == Token.UNSIGNEDFLOAT:
|
||||
data_values.append(sign*eval(token.lexeme))
|
||||
elif token.category == Token.MINUS:
|
||||
sign = -1
|
||||
#else:
|
||||
#data_values.append(token.lexeme)
|
||||
else:
|
||||
sign = 1
|
||||
|
||||
|
||||
return data_values
|
||||
|
||||
def restore(self,restoreLineNo):
|
||||
if restoreLineNo == 0 or restoreLineNo in self.__datastmts:
|
||||
|
||||
if restoreLineNo == 0:
|
||||
self.__next_data = restoreLineNo
|
||||
else:
|
||||
|
||||
line_numbers = list(self.__datastmts.keys())
|
||||
line_numbers.sort()
|
||||
|
||||
indexln = line_numbers.index(restoreLineNo)
|
||||
|
||||
if indexln == 0:
|
||||
self.__next_data = 0
|
||||
else:
|
||||
self.__next_data = line_numbers[indexln-1]
|
||||
else:
|
||||
raise RuntimeError('Attempt to RESTORE but no DATA ' +
|
||||
'statement at line ' + str(restoreLineNo))
|
||||
|
||||
|
||||
class Program:
|
||||
|
||||
def __init__(self):
|
||||
# Dictionary to represent program
|
||||
# statements, keyed by line number
|
||||
self.__program = {}
|
||||
|
||||
# Program counter
|
||||
self.__next_stmt = 0
|
||||
|
||||
# Initialise return stack for subroutine returns
|
||||
self.__return_stack = []
|
||||
|
||||
# return dictionary for loop returns
|
||||
self.__return_loop = {}
|
||||
|
||||
# Setup DATA object
|
||||
self.__data = BASICData()
|
||||
|
||||
def __str__(self):
|
||||
|
||||
program_text = ""
|
||||
line_numbers = self.line_numbers()
|
||||
|
||||
for line_number in line_numbers:
|
||||
program_text += self.str_statement(line_number)
|
||||
|
||||
return program_text
|
||||
|
||||
def str_statement(self, line_number):
|
||||
line_text = str(line_number) + " "
|
||||
|
||||
statement = self.__program[line_number]
|
||||
if statement[0].category == Token.DATA:
|
||||
statement = self.__data.getTokens(line_number)
|
||||
for token in statement:
|
||||
# Add in quotes for strings
|
||||
if token.category == Token.STRING:
|
||||
line_text += '"' + token.lexeme + '" '
|
||||
|
||||
else:
|
||||
line_text += token.lexeme + " "
|
||||
line_text += "\n"
|
||||
return line_text
|
||||
|
||||
def list(self, start_line=None, end_line=None):
|
||||
"""Lists the program"""
|
||||
line_numbers = self.line_numbers()
|
||||
if not start_line:
|
||||
start_line = int(line_numbers[0])
|
||||
|
||||
if not end_line:
|
||||
end_line = int(line_numbers[-1])
|
||||
|
||||
for line_number in line_numbers:
|
||||
if int(line_number) >= start_line and int(line_number) <= end_line:
|
||||
print(self.str_statement(line_number), end="")
|
||||
|
||||
def save(self, file):
|
||||
"""Save the program
|
||||
|
||||
:param file: The name and path of the save file, .bas is
|
||||
appended
|
||||
|
||||
"""
|
||||
if not file.lower().endswith(".bas"):
|
||||
file += ".bas"
|
||||
try:
|
||||
with open(file, "w") as outfile:
|
||||
outfile.write(str(self))
|
||||
except OSError:
|
||||
raise OSError("Could not save to file")
|
||||
|
||||
def load(self, file):
|
||||
"""Load the program
|
||||
|
||||
:param file: The name and path of the file to be loaded, .bas is
|
||||
appended
|
||||
|
||||
"""
|
||||
|
||||
# New out the program
|
||||
self.delete()
|
||||
if not file.lower().endswith(".bas"):
|
||||
file += ".bas"
|
||||
try:
|
||||
lexer = Lexer()
|
||||
with open(file, "r") as infile:
|
||||
for line in infile:
|
||||
line = line.replace("\r", "").replace("\n", "").strip()
|
||||
tokenlist = lexer.tokenize(line)
|
||||
self.add_stmt(tokenlist)
|
||||
|
||||
except OSError:
|
||||
raise OSError("Could not read file")
|
||||
|
||||
if file.rfind('/') == 0:
|
||||
os.chdir("/")
|
||||
elif file.rfind('/') != -1:
|
||||
os.chdir(file[:file.rfind('/')])
|
||||
|
||||
def add_stmt(self, tokenlist):
|
||||
"""
|
||||
Adds the supplied token list
|
||||
to the program. The first token should
|
||||
be the line number. If a token list with the
|
||||
same line number already exists, this is
|
||||
replaced.
|
||||
|
||||
:param tokenlist: List of BTokens representing a
|
||||
numbered program statement
|
||||
|
||||
"""
|
||||
if len(tokenlist) > 0:
|
||||
try:
|
||||
line_number = int(tokenlist[0].lexeme)
|
||||
if tokenlist[1].lexeme == "DATA":
|
||||
self.__data.addData(line_number,tokenlist[1:])
|
||||
self.__program[line_number] = [tokenlist[1],]
|
||||
else:
|
||||
self.__program[line_number] = tokenlist[1:]
|
||||
|
||||
except TypeError as err:
|
||||
raise TypeError("Invalid line number: " +
|
||||
str(err))
|
||||
|
||||
def line_numbers(self):
|
||||
"""Returns a list of all the
|
||||
line numbers for the program,
|
||||
sorted
|
||||
|
||||
:return: A sorted list of
|
||||
program line numbers
|
||||
"""
|
||||
line_numbers = list(self.__program.keys())
|
||||
line_numbers.sort()
|
||||
|
||||
return line_numbers
|
||||
|
||||
def __execute(self, line_number):
|
||||
"""Execute the statement with the
|
||||
specified line number
|
||||
|
||||
:param line_number: The line number
|
||||
|
||||
:return: The FlowSignal to indicate to the program
|
||||
how to branch if necessary, None otherwise
|
||||
|
||||
"""
|
||||
if line_number not in self.__program.keys():
|
||||
raise RuntimeError("Line number " + line_number +
|
||||
" does not exist")
|
||||
|
||||
statement = self.__program[line_number]
|
||||
|
||||
try:
|
||||
return self.__parser.parse(statement, line_number)
|
||||
|
||||
except RuntimeError as err:
|
||||
raise RuntimeError(str(err))
|
||||
|
||||
def execute(self):
|
||||
"""Execute the program"""
|
||||
|
||||
self.__parser = BASICParser(self.__data)
|
||||
self.__data.restore(0) # reset data pointer
|
||||
|
||||
line_numbers = self.line_numbers()
|
||||
|
||||
if len(line_numbers) > 0:
|
||||
# Set up an index into the ordered list
|
||||
# of line numbers that can be used for
|
||||
# sequential statement execution. The index
|
||||
# will be incremented by one, unless modified by
|
||||
# a jump
|
||||
index = 0
|
||||
self.set_next_line_number(line_numbers[index])
|
||||
|
||||
# Run through the program until the
|
||||
# has line number has been reached
|
||||
while True:
|
||||
flowsignal = self.__execute(self.get_next_line_number())
|
||||
self.__parser.last_flowsignal = flowsignal
|
||||
|
||||
if flowsignal:
|
||||
if flowsignal.ftype == FlowSignal.SIMPLE_JUMP:
|
||||
# GOTO or conditional branch encountered
|
||||
try:
|
||||
index = line_numbers.index(flowsignal.ftarget)
|
||||
|
||||
except ValueError:
|
||||
raise RuntimeError("Invalid line number supplied in GOTO or conditional branch: "
|
||||
+ str(flowsignal.ftarget))
|
||||
|
||||
self.set_next_line_number(flowsignal.ftarget)
|
||||
|
||||
elif flowsignal.ftype == FlowSignal.GOSUB:
|
||||
# Subroutine call encountered
|
||||
# Add line number of next instruction to
|
||||
# the return stack
|
||||
if index + 1 < len(line_numbers):
|
||||
self.__return_stack.append(line_numbers[index + 1])
|
||||
|
||||
else:
|
||||
raise RuntimeError("GOSUB at end of program, nowhere to return")
|
||||
|
||||
# Set the index to be the subroutine start line
|
||||
# number
|
||||
try:
|
||||
index = line_numbers.index(flowsignal.ftarget)
|
||||
|
||||
except ValueError:
|
||||
raise RuntimeError("Invalid line number supplied in subroutine call: "
|
||||
+ str(flowsignal.ftarget))
|
||||
|
||||
self.set_next_line_number(flowsignal.ftarget)
|
||||
|
||||
elif flowsignal.ftype == FlowSignal.RETURN:
|
||||
# Subroutine return encountered
|
||||
# Pop return address from the stack
|
||||
try:
|
||||
index = line_numbers.index(self.__return_stack.pop())
|
||||
|
||||
except ValueError:
|
||||
raise RuntimeError("Invalid subroutine return in line " +
|
||||
str(self.get_next_line_number()))
|
||||
|
||||
except IndexError:
|
||||
raise RuntimeError("RETURN encountered without corresponding " +
|
||||
"subroutine call in line " + str(self.get_next_line_number()))
|
||||
|
||||
self.set_next_line_number(line_numbers[index])
|
||||
|
||||
elif flowsignal.ftype == FlowSignal.STOP:
|
||||
break
|
||||
|
||||
elif flowsignal.ftype == FlowSignal.LOOP_BEGIN:
|
||||
# Loop start encountered
|
||||
# Put loop line number on the stack so
|
||||
# that it can be returned to when the loop
|
||||
# repeats
|
||||
self.__return_loop[flowsignal.floop_var] = line_numbers[index]
|
||||
|
||||
# Continue to the next statement in the loop
|
||||
index = index + 1
|
||||
|
||||
if index < len(line_numbers):
|
||||
self.set_next_line_number(line_numbers[index])
|
||||
|
||||
else:
|
||||
# Reached end of program
|
||||
raise RuntimeError("Program terminated within a loop")
|
||||
|
||||
elif flowsignal.ftype == FlowSignal.LOOP_SKIP:
|
||||
# Loop variable has reached end value, so ignore
|
||||
# all statements within loop and move past the corresponding
|
||||
# NEXT statement
|
||||
index = index + 1
|
||||
while index < len(line_numbers):
|
||||
next_line_number = line_numbers[index]
|
||||
temp_tokenlist = self.__program[next_line_number]
|
||||
|
||||
if temp_tokenlist[0].category == Token.NEXT and \
|
||||
len(temp_tokenlist) > 1:
|
||||
# Check the loop variable to ensure we have not found
|
||||
# the NEXT statement for a nested loop
|
||||
if temp_tokenlist[1].lexeme == flowsignal.ftarget:
|
||||
# Move the statement after this NEXT, if there
|
||||
# is one
|
||||
index = index + 1
|
||||
if index < len(line_numbers):
|
||||
next_line_number = line_numbers[index] # Statement after the NEXT
|
||||
self.set_next_line_number(next_line_number)
|
||||
break
|
||||
|
||||
index = index + 1
|
||||
|
||||
# Check we have not reached end of program
|
||||
if index >= len(line_numbers):
|
||||
# Terminate the program
|
||||
break
|
||||
|
||||
elif flowsignal.ftype == FlowSignal.LOOP_REPEAT:
|
||||
# Loop repeat encountered
|
||||
# Pop the loop start address from the stack
|
||||
try:
|
||||
index = line_numbers.index(self.__return_loop.pop(flowsignal.floop_var))
|
||||
|
||||
except ValueError:
|
||||
raise RuntimeError("Invalid loop exit in line " +
|
||||
str(self.get_next_line_number()))
|
||||
|
||||
except KeyError:
|
||||
raise RuntimeError("NEXT encountered without corresponding " +
|
||||
"FOR loop in line " + str(self.get_next_line_number()))
|
||||
|
||||
self.set_next_line_number(line_numbers[index])
|
||||
|
||||
else:
|
||||
index = index + 1
|
||||
|
||||
if index < len(line_numbers):
|
||||
self.set_next_line_number(line_numbers[index])
|
||||
|
||||
else:
|
||||
# Reached end of program
|
||||
break
|
||||
|
||||
else:
|
||||
raise RuntimeError("No statements to execute")
|
||||
|
||||
def delete(self):
|
||||
"""Deletes the program by emptying the dictionary"""
|
||||
self.__program.clear()
|
||||
self.__data.delete()
|
||||
|
||||
def delete_statement(self, line_number):
|
||||
"""Deletes a statement from the program with
|
||||
the specified line number, if it exists
|
||||
|
||||
:param line_number: The line number to be deleted
|
||||
|
||||
"""
|
||||
self.__data.delData(line_number)
|
||||
try:
|
||||
del self.__program[line_number]
|
||||
|
||||
except KeyError:
|
||||
raise KeyError("Line number does not exist")
|
||||
|
||||
def get_next_line_number(self):
|
||||
"""Returns the line number of the next statement
|
||||
to be executed
|
||||
|
||||
:return: The line number
|
||||
|
||||
"""
|
||||
|
||||
return self.__next_stmt
|
||||
|
||||
def set_next_line_number(self, line_number):
|
||||
"""Sets the line number of the next
|
||||
statement to be executed
|
||||
|
||||
:param line_number: The new line number
|
||||
|
||||
"""
|
||||
self.__next_stmt = line_number
|
||||
BIN
builtin_apps/PyDOS/PyDOS.bmp
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
1396
builtin_apps/PyDOS/PyDOS.py
Normal file
6
builtin_apps/PyDOS/code.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Launches the PyDOS shell
|
||||
#
|
||||
# This is done in a sperate file so that __name__ is set to "PyDOS" instead of "__main__"
|
||||
# which helps some modules detect that they are running in PyDOS.
|
||||
|
||||
import PyDOS
|
||||
4
builtin_apps/PyDOS/metadata.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"title": "PyDOS",
|
||||
"icon": "PyDOS.bmp"
|
||||
}
|
||||
3
builtin_apps/editor/adafruit_editor/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-FileCopyrightText: 2024 Tim Cocks
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
150
builtin_apps/editor/adafruit_editor/dang.py
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import select
|
||||
import sys
|
||||
# pylint: disable=no-self-use
|
||||
|
||||
try:
|
||||
import termios
|
||||
|
||||
_orig_attr = None # pylint: disable=invalid-name
|
||||
|
||||
def _nonblocking():
|
||||
global _orig_attr # pylint: disable=global-statement
|
||||
_orig_attr = termios.tcgetattr(sys.stdin)
|
||||
attr = termios.tcgetattr(sys.stdin)
|
||||
attr[3] &= ~(termios.ECHO | termios.ICANON)
|
||||
attr[6][termios.VMIN] = 1
|
||||
attr[6][termios.VTIME] = 0
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr)
|
||||
|
||||
def _blocking():
|
||||
if _orig_attr is not None:
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, _orig_attr)
|
||||
|
||||
except ImportError:
|
||||
|
||||
def _nonblocking():
|
||||
pass
|
||||
|
||||
def _blocking():
|
||||
pass
|
||||
|
||||
|
||||
# LINES = 24
|
||||
# COLS = 80
|
||||
|
||||
LINES = 33
|
||||
COLS = 120
|
||||
|
||||
special_keys = {
|
||||
"\x1b": ..., # all prefixes of special keys must be entered as Ellipsis
|
||||
"\x1b[": ...,
|
||||
"\x1b[3": ...,
|
||||
"\x1b[5": ...,
|
||||
"\x1b[6": ...,
|
||||
"\x1b[A": "KEY_UP",
|
||||
"\x1b[B": "KEY_DOWN",
|
||||
"\x1b[C": "KEY_RIGHT",
|
||||
"\x1b[D": "KEY_LEFT",
|
||||
"\x1b[H": "KEY_HOME",
|
||||
"\x1b[F": "KEY_END",
|
||||
"\x1b[5~": "KEY_PGUP",
|
||||
"\x1b[6~": "KEY_PGDN",
|
||||
"\x1b[3~": "KEY_DELETE",
|
||||
}
|
||||
|
||||
|
||||
class Screen:
|
||||
def __init__(self, terminal=None):
|
||||
self._poll = select.poll()
|
||||
self._poll.register(sys.stdin, select.POLLIN)
|
||||
self._pending = ""
|
||||
self._terminal = terminal
|
||||
|
||||
def _sys_stdin_readable(self):
|
||||
return hasattr(sys.stdin, "readable") and sys.stdin.readable()
|
||||
|
||||
def _sys_stdout_flush(self):
|
||||
if hasattr(sys.stdout, "flush"):
|
||||
sys.stdout.flush()
|
||||
|
||||
def _terminal_read_blocking(self):
|
||||
return sys.stdin.read(1)
|
||||
|
||||
def _terminal_read_timeout(self, timeout):
|
||||
if self._sys_stdin_readable() or self._poll.poll(timeout):
|
||||
r = sys.stdin.read(1)
|
||||
return r
|
||||
return None
|
||||
|
||||
def move(self, y, x):
|
||||
if self._terminal is not None:
|
||||
self._terminal.write(f"\033[{y+1};{x+1}H")
|
||||
else:
|
||||
print(end=f"\033[{y+1};{x+1}H")
|
||||
|
||||
def erase(self):
|
||||
if self._terminal is not None:
|
||||
self._terminal.write("\033H\033[2J")
|
||||
else:
|
||||
print(end="\033H\033[2J")
|
||||
|
||||
def addstr(self, y, x, text):
|
||||
self.move(y, x)
|
||||
if self._terminal is not None:
|
||||
self._terminal.write(text)
|
||||
else:
|
||||
print(end=text)
|
||||
|
||||
def getkey(self):
|
||||
self._sys_stdout_flush()
|
||||
pending = self._pending
|
||||
if pending and (code := special_keys.get(pending)) is None:
|
||||
self._pending = pending[1:]
|
||||
return pending[0]
|
||||
|
||||
while True:
|
||||
if pending:
|
||||
c = self._terminal_read_timeout(50)
|
||||
if c is None:
|
||||
self._pending = pending[1:]
|
||||
return pending[0]
|
||||
else:
|
||||
c = self._terminal_read_timeout(50)
|
||||
if c is None:
|
||||
return None
|
||||
c = pending + c
|
||||
|
||||
code = special_keys.get(c)
|
||||
|
||||
if code is None:
|
||||
self._pending = c[1:]
|
||||
return c[0]
|
||||
if code is not Ellipsis:
|
||||
return code
|
||||
|
||||
pending = c
|
||||
|
||||
|
||||
def wrapper(func, *args, **kwds):
|
||||
stdscr = Screen()
|
||||
try:
|
||||
_nonblocking()
|
||||
return func(stdscr, *args, **kwds)
|
||||
finally:
|
||||
_blocking()
|
||||
stdscr.move(LINES - 1, 0)
|
||||
print("\n")
|
||||
|
||||
def custom_terminal_wrapper(terminal, func, *args, **kwds):
|
||||
stdscr = Screen(terminal)
|
||||
try:
|
||||
_nonblocking()
|
||||
return func(stdscr, *args, **kwds)
|
||||
finally:
|
||||
_blocking()
|
||||
stdscr.move(LINES - 1, 0)
|
||||
print("\n")
|
||||
547
builtin_apps/editor/adafruit_editor/editor.py
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
# SPDX-FileCopyrightText: 2020 Wasim Lorgat
|
||||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import gc
|
||||
import os
|
||||
|
||||
import microcontroller
|
||||
import supervisor
|
||||
import usb_cdc
|
||||
from . import dang as curses
|
||||
from . import util
|
||||
from adafruit_pathlib import Path
|
||||
import time
|
||||
import json
|
||||
from adafruit_argv_file import argv_filename
|
||||
|
||||
# pylint: disable=redefined-builtin
|
||||
|
||||
# def print(message):
|
||||
# usb_cdc.data.write(f"{message}\r\n".encode("utf-8"))
|
||||
|
||||
INPUT_DISPLAY_REFRESH_COOLDOWN = 0.3 # s
|
||||
SHOW_MEMFREE = False
|
||||
|
||||
|
||||
class MaybeDisableReload:
|
||||
def __enter__(self):
|
||||
try:
|
||||
from supervisor import runtime # pylint: disable=import-outside-toplevel
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
self._old_autoreload = ( # pylint: disable=attribute-defined-outside-init
|
||||
runtime.autoreload
|
||||
)
|
||||
runtime.autoreload = False
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
try:
|
||||
from supervisor import runtime # pylint: disable=import-outside-toplevel
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
runtime.autoreload = self._old_autoreload
|
||||
|
||||
|
||||
def os_exists(filename):
|
||||
try:
|
||||
os.stat(filename)
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def gc_mem_free_hint():
|
||||
if not SHOW_MEMFREE:
|
||||
return ""
|
||||
if hasattr(gc, "mem_free"):
|
||||
gc.collect()
|
||||
return f" | free: {gc.mem_free()}"
|
||||
return ""
|
||||
|
||||
|
||||
class Buffer:
|
||||
def __init__(self, lines):
|
||||
self.lines = lines
|
||||
|
||||
def __len__(self):
|
||||
return len(self.lines)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.lines[index]
|
||||
|
||||
@property
|
||||
def bottom(self):
|
||||
return len(self) - 1
|
||||
|
||||
def insert(self, cursor, string):
|
||||
row, col = cursor.row, cursor.col
|
||||
# print(f"len: {len(self.lines)}")
|
||||
# print(f"row: {row}")
|
||||
try:
|
||||
current = self.lines.pop(row)
|
||||
except IndexError:
|
||||
current = ""
|
||||
new = current[:col] + string + current[col:]
|
||||
self.lines.insert(row, new)
|
||||
|
||||
def split(self, cursor):
|
||||
row, col = cursor.row, cursor.col
|
||||
current = self.lines.pop(row)
|
||||
self.lines.insert(row, current[:col])
|
||||
self.lines.insert(row + 1, current[col:])
|
||||
|
||||
def delete(self, cursor):
|
||||
row, col = cursor.row, cursor.col
|
||||
if (row, col) < (self.bottom, len(self[row])):
|
||||
current = self.lines.pop(row)
|
||||
if col < len(current):
|
||||
new = current[:col] + current[col + 1:]
|
||||
self.lines.insert(row, new)
|
||||
else:
|
||||
nextline = self.lines.pop(row)
|
||||
new = current + nextline
|
||||
self.lines.insert(row, new)
|
||||
|
||||
|
||||
def clamp(x, lower, upper):
|
||||
if x < lower:
|
||||
return lower
|
||||
if x > upper:
|
||||
return upper
|
||||
return x
|
||||
|
||||
|
||||
def _count_leading_characters(text, char):
|
||||
count = 0
|
||||
for c in text:
|
||||
if c == char:
|
||||
count += 1
|
||||
else:
|
||||
break
|
||||
return count
|
||||
|
||||
|
||||
class Cursor:
|
||||
def __init__(self, row=0, col=0, col_hint=None):
|
||||
self.row = row
|
||||
self._col = col
|
||||
self._col_hint = col if col_hint is None else col_hint
|
||||
|
||||
@property
|
||||
def col(self):
|
||||
return self._col
|
||||
|
||||
@col.setter
|
||||
def col(self, col):
|
||||
self._col = col
|
||||
self._col_hint = col
|
||||
|
||||
def _clamp_col(self, buffer):
|
||||
self._col = min(self._col_hint, len(buffer[self.row]))
|
||||
|
||||
def up(self, buffer): # pylint: disable=invalid-name
|
||||
if self.row > 0:
|
||||
self.row -= 1
|
||||
self._clamp_col(buffer)
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
|
||||
def down(self, buffer):
|
||||
if self.row < len(buffer) - 1:
|
||||
self.row += 1
|
||||
self._clamp_col(buffer)
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
|
||||
def left(self, buffer):
|
||||
if self.col > 0:
|
||||
self.col -= 1
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
elif self.row > 0:
|
||||
self.row -= 1
|
||||
self.col = len(buffer[self.row])
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
|
||||
def right(self, buffer):
|
||||
# print(f"len: {len(buffer)}")
|
||||
if len(buffer) > 0 and self.col < len(buffer[self.row]):
|
||||
self.col += 1
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
elif self.row < len(buffer) - 1:
|
||||
self.row += 1
|
||||
self.col = 0
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
|
||||
def end(self, buffer):
|
||||
self.col = len(buffer[self.row])
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
|
||||
|
||||
class Window:
|
||||
def __init__(self, n_rows, n_cols, row=0, col=0):
|
||||
self.n_rows = n_rows
|
||||
self.n_cols = n_cols
|
||||
self.row = row
|
||||
self.col = col
|
||||
|
||||
@property
|
||||
def bottom(self):
|
||||
return self.row + self.n_rows - 1
|
||||
|
||||
def up(self, cursor): # pylint: disable=invalid-name
|
||||
if cursor.row == self.row - 1 and self.row > 0:
|
||||
self.row -= 1
|
||||
|
||||
def down(self, buffer, cursor):
|
||||
if cursor.row == self.bottom + 1 and self.bottom < len(buffer) - 1:
|
||||
self.row += 1
|
||||
|
||||
def horizontal_scroll(self, cursor, left_margin=5, right_margin=2):
|
||||
n_pages = cursor.col // (self.n_cols - right_margin)
|
||||
self.col = max(n_pages * self.n_cols - right_margin - left_margin, 0)
|
||||
|
||||
def translate(self, cursor):
|
||||
return cursor.row - self.row, cursor.col - self.col
|
||||
|
||||
|
||||
def left(window, buffer, cursor):
|
||||
cursor.left(buffer)
|
||||
window.up(cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def right(window, buffer, cursor):
|
||||
cursor.right(buffer)
|
||||
window.down(buffer, cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def home(window, buffer, cursor): # pylint: disable=unused-argument
|
||||
cursor.col = 0
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def end(window, buffer, cursor):
|
||||
cursor.end(buffer)
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def editor(stdscr, filename, mouse=None, terminal_tilegrid=None): # pylint: disable=too-many-branches,too-many-statements
|
||||
|
||||
def _only_spaces_before(cursor):
|
||||
i = cursor.col - 1
|
||||
while i >= 0:
|
||||
print(f"i: {i} chr: '{buffer.lines[cursor.row][i]}'")
|
||||
if buffer.lines[cursor.row][i] != " ":
|
||||
return False
|
||||
i -= 1
|
||||
return True
|
||||
if os_exists(filename):
|
||||
with open(filename, "r", encoding="utf-8") as f:
|
||||
buffer = Buffer(f.read().splitlines())
|
||||
else:
|
||||
buffer = Buffer([""])
|
||||
print(f"cwd: {os.getcwd()} | {os.getcwd() == "/apps/editor"}")
|
||||
if os.getcwd() != "/apps/editor" and os.getcwd() != "/":
|
||||
absolute_filepath = os.getcwd() + "/" + filename
|
||||
else:
|
||||
absolute_filepath = filename
|
||||
|
||||
user_message = None
|
||||
user_message_shown_time = -1
|
||||
user_prompt = None
|
||||
user_response = ""
|
||||
last_find = ""
|
||||
find_command = False
|
||||
goto_command = False
|
||||
|
||||
clicked_tile_coords = [None, None]
|
||||
|
||||
window = Window(curses.LINES - 1, curses.COLS - 1)
|
||||
cursor = Cursor()
|
||||
terminal_tilegrid.pixel_shader[cursor.col,cursor.row] = [1, 0]
|
||||
old_cursor_pos = (cursor.col, cursor.row)
|
||||
old_window_pos = (window.col, window.row)
|
||||
# try:
|
||||
# visible_cursor.text = buffer[0][0]
|
||||
# except IndexError:
|
||||
# visible_cursor.text = " "
|
||||
|
||||
stdscr.erase()
|
||||
|
||||
img = [None] * curses.LINES
|
||||
|
||||
def setline(row, line):
|
||||
if img[row] == line:
|
||||
return
|
||||
img[row] = line
|
||||
line += " " * (window.n_cols - len(line))
|
||||
stdscr.addstr(row, 0, line)
|
||||
|
||||
print(f"cwd: {os.getcwd()} | abs path: {absolute_filepath} | filename: {filename}")
|
||||
while True:
|
||||
lastrow = 0
|
||||
for row, line in enumerate(buffer[window.row: window.row + window.n_rows]):
|
||||
lastrow = row
|
||||
if row == cursor.row - window.row and window.col > 0:
|
||||
line = "«" + line[window.col + 1:]
|
||||
if len(line) > window.n_cols:
|
||||
line = line[: window.n_cols - 1] + "»"
|
||||
setline(row, line)
|
||||
for row in range(lastrow + 1, window.n_rows):
|
||||
setline(row, "~~ EOF ~~")
|
||||
row = curses.LINES - 1
|
||||
|
||||
if user_message is None and user_prompt is None:
|
||||
if (not absolute_filepath.startswith("/saves/") and
|
||||
not absolute_filepath.startswith("/sd/") and
|
||||
util.readonly()):
|
||||
|
||||
line = f"{absolute_filepath:12} (mnt RO ^W) | ^R Run | ^O Open | ^F Find | ^G GoTo | ^C quit {gc_mem_free_hint()}"
|
||||
else:
|
||||
line = f"{absolute_filepath:12} (mnt RW ^W) | ^R Run | ^O Open | ^F Find | ^G GoTo | ^S Save | ^X save & eXit | ^C quit {gc_mem_free_hint()}"
|
||||
line = line + " " * (window.n_cols - len(line))
|
||||
line = line[:window.n_cols-len(f'{cursor.row+1},{cursor.col+1}')] + f"{cursor.row+1},{cursor.col+1}"
|
||||
|
||||
elif user_message is not None:
|
||||
line = user_message
|
||||
if user_message_shown_time + 3.0 < time.monotonic():
|
||||
user_message = None
|
||||
elif user_prompt is not None:
|
||||
line = f'{user_prompt} {user_response}'
|
||||
setline(row, line)
|
||||
|
||||
stdscr.move(*window.translate(cursor))
|
||||
|
||||
# display.refresh(minimum_frames_per_second=20)
|
||||
k = stdscr.getkey()
|
||||
if k is not None:
|
||||
# print(repr(k))
|
||||
if user_prompt is not None:
|
||||
if len(k) == 1 and " " <= k <= "~":
|
||||
user_response += k
|
||||
elif k == "\n":
|
||||
user_prompt = None
|
||||
|
||||
if find_command:
|
||||
found = False
|
||||
if user_response == "":
|
||||
user_response = last_find
|
||||
if user_response:
|
||||
last_find = user_response
|
||||
if buffer[cursor.row][min(cursor.col+1,len(buffer[cursor.row])-1):].find(user_response) != -1:
|
||||
found = True
|
||||
user_message = f"Found '{user_response}' in line {cursor.row+1}"
|
||||
cursor.col += buffer[cursor.row][cursor.col:].find(user_response) - 1
|
||||
right(window, buffer, cursor)
|
||||
else:
|
||||
for r, line in enumerate(buffer[cursor.row+1:]):
|
||||
if line.find(user_response) != -1:
|
||||
found = True
|
||||
user_message = f"Found '{user_response}' in line {r + cursor.row + 2}"
|
||||
cursor.row = clamp(r + cursor.row + 1, 0, len(buffer) - 1)
|
||||
window.row = clamp(cursor.row - window.n_rows // 2, 0, len(buffer) - window.n_rows)
|
||||
cursor.col = line.find(user_response) - 1
|
||||
right(window, buffer, cursor)
|
||||
break
|
||||
|
||||
if not found:
|
||||
user_message = f"'{user_response}' not found"
|
||||
user_message_shown_time = time.monotonic()
|
||||
|
||||
user_response = ""
|
||||
find_command = False
|
||||
|
||||
elif goto_command:
|
||||
if user_response.isdigit():
|
||||
cursor.row = clamp(int(user_response) - 1, 0, len(buffer) - 1)
|
||||
window.row = clamp(cursor.row - window.n_rows // 2, 0, len(buffer) - window.n_rows)
|
||||
cursor.col = 0
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
user_response = ""
|
||||
goto_command = False
|
||||
|
||||
elif k == "\x7f" or k == "\x08": # backspace
|
||||
user_response = user_response[:-1]
|
||||
elif k == "\x1b": # escape
|
||||
user_prompt = None
|
||||
user_response = ""
|
||||
else:
|
||||
print(f"unhandled k: {k}")
|
||||
print(f"unhandled K: {ord(k)}")
|
||||
print(f"unhandled k: {bytes(k, 'utf-8')}")
|
||||
|
||||
elif len(k) == 1 and " " <= k <= "~":
|
||||
buffer.insert(cursor, k)
|
||||
for _ in k:
|
||||
right(window, buffer, cursor)
|
||||
elif k == "\x18": # ctrl-x
|
||||
if not util.readonly():
|
||||
with open(filename, "w", encoding="utf-8") as f:
|
||||
for row in buffer:
|
||||
f.write(f"{row}\n")
|
||||
return
|
||||
else:
|
||||
print("Unable to Save due to readonly mode! File Contents:")
|
||||
print("---- begin file contents ----")
|
||||
for row in buffer:
|
||||
print(row)
|
||||
print("---- end file contents ----")
|
||||
elif k == "\x13": # Ctrl-S
|
||||
print(absolute_filepath)
|
||||
print(f"starts with saves: {absolute_filepath.startswith("/saves/")}")
|
||||
print(f"stars saves: {absolute_filepath.startswith("/saves/")}")
|
||||
print(f"stars sd: {absolute_filepath.startswith("/sd/")}")
|
||||
print(f"readonly: {util.readonly()}")
|
||||
if (absolute_filepath.startswith("/saves/") or
|
||||
absolute_filepath.startswith("/sd/") or
|
||||
not util.readonly()):
|
||||
|
||||
with open(absolute_filepath, "w", encoding="utf-8") as f:
|
||||
for row in buffer:
|
||||
f.write(f"{row}\n")
|
||||
user_message = "Saved"
|
||||
user_message_shown_time = time.monotonic()
|
||||
else:
|
||||
user_message = "Unable to Save due to readonly mode!"
|
||||
user_message_shown_time = time.monotonic()
|
||||
elif k == "\x11": # Ctrl-Q
|
||||
print("ctrl-Q")
|
||||
for row in buffer:
|
||||
print(row)
|
||||
elif k == "\x17": # Ctrl-W
|
||||
boot_args_file = argv_filename("/boot.py")
|
||||
with open(boot_args_file, "w") as f:
|
||||
f.write(json.dumps([not util.readonly(), "/apps/editor/code.py", Path(filename).absolute()]))
|
||||
microcontroller.reset()
|
||||
elif k == "\x12": # Ctrl-R
|
||||
print(f"Run: {filename}")
|
||||
|
||||
launcher_code_args_file = argv_filename("/code.py")
|
||||
with open(launcher_code_args_file, "w") as f:
|
||||
f.write(json.dumps(["/apps/editor/code.py", Path(filename).absolute()]))
|
||||
|
||||
supervisor.set_next_code_file(filename, sticky_on_reload=False, reload_on_error=True,
|
||||
working_directory=Path(filename).parent.absolute())
|
||||
supervisor.reload()
|
||||
elif k == "\x0f": # Ctrl-O
|
||||
|
||||
supervisor.set_next_code_file("/apps/editor/code.py", sticky_on_reload=False, reload_on_error=True,
|
||||
working_directory="/apps/editor")
|
||||
supervisor.reload()
|
||||
elif k == "\x06": # Ctrl-F
|
||||
find_command = True
|
||||
if last_find == "":
|
||||
user_prompt = "Find:"
|
||||
else:
|
||||
user_prompt = f"Find: [{last_find}]"
|
||||
elif k == "\x07": # Ctrl-G
|
||||
goto_command = True
|
||||
user_prompt = "Goto line:"
|
||||
|
||||
elif k == "KEY_HOME":
|
||||
home(window, buffer, cursor)
|
||||
elif k == "KEY_END":
|
||||
end(window, buffer, cursor)
|
||||
elif k == "KEY_LEFT":
|
||||
left(window, buffer, cursor)
|
||||
elif k == "KEY_DOWN":
|
||||
cursor.down(buffer)
|
||||
window.down(buffer, cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
print(f"scroll pos: {window.row}")
|
||||
elif k == "KEY_PGDN":
|
||||
for _ in range(window.n_rows):
|
||||
cursor.down(buffer)
|
||||
window.down(buffer, cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_UP":
|
||||
cursor.up(buffer)
|
||||
window.up(cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_PGUP":
|
||||
for _ in range(window.n_rows):
|
||||
cursor.up(buffer)
|
||||
window.up(cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_RIGHT":
|
||||
right(window, buffer, cursor)
|
||||
elif k == "\n":
|
||||
leading_spaces = _count_leading_characters(buffer.lines[cursor.row], " ")
|
||||
buffer.split(cursor)
|
||||
right(window, buffer, cursor)
|
||||
for i in range(leading_spaces):
|
||||
buffer.insert(cursor, " ")
|
||||
right(window, buffer, cursor)
|
||||
elif k in ("KEY_DELETE", "\x04"):
|
||||
print("delete")
|
||||
if cursor.row < len(buffer.lines) - 1 or \
|
||||
cursor.col < len(buffer.lines[cursor.row]):
|
||||
buffer.delete(cursor)
|
||||
# try:
|
||||
# visible_cursor.text = buffer.lines[cursor.row][cursor.col]
|
||||
# except IndexError:
|
||||
# visible_cursor.text = " "
|
||||
|
||||
elif k in ("KEY_BACKSPACE", "\x7f", "\x08"):
|
||||
print(f"backspace {bytes(k, 'utf-8')}")
|
||||
if (cursor.row, cursor.col) > (0, 0):
|
||||
if cursor.col > 0 and buffer.lines[cursor.row][cursor.col-1] == " " and _only_spaces_before(cursor):
|
||||
for i in range(4):
|
||||
left(window, buffer, cursor)
|
||||
buffer.delete(cursor)
|
||||
else:
|
||||
left(window, buffer, cursor)
|
||||
buffer.delete(cursor)
|
||||
|
||||
else:
|
||||
print(f"unhandled k: {k}")
|
||||
print(f"unhandled K: {ord(k)}")
|
||||
print(f"unhandled k: {bytes(k, 'utf-8')}")
|
||||
|
||||
|
||||
if mouse is not None:
|
||||
pressed_btns = mouse.update()
|
||||
if pressed_btns is not None and "left" in pressed_btns:
|
||||
clicked_tile_coords[0] = mouse.x // 6
|
||||
clicked_tile_coords[1] = mouse.y // 12
|
||||
|
||||
if clicked_tile_coords[0] > len(buffer.lines[clicked_tile_coords[1]+window.row]):
|
||||
clicked_tile_coords[0] = len(buffer.lines[clicked_tile_coords[1]+window.row])
|
||||
cursor.row = clicked_tile_coords[1] + window.row
|
||||
cursor.col = clicked_tile_coords[0] + window.col
|
||||
|
||||
# print("updating visible cursor")
|
||||
# print(f"anchored pos: {((cursor.col * 6) - 1, (cursor.row * 12) + 20)}")
|
||||
|
||||
if (old_cursor_pos[0] - old_window_pos[0] != cursor.col - window.col or
|
||||
old_cursor_pos[1] - old_window_pos[1] != cursor.row - window.row):
|
||||
# print(f"old cursor: {old_cursor_pos}, new: {(cursor.col, cursor.row)}")
|
||||
# print(f"window (row,col): {window.row}, {window.col}")
|
||||
terminal_tilegrid.pixel_shader[old_cursor_pos[0] - old_window_pos[0], old_cursor_pos[1] - old_window_pos[1]] = [0,1]
|
||||
terminal_tilegrid.pixel_shader[cursor.col - window.col, cursor.row - window.row] = [1,0]
|
||||
# print(f"old: {terminal_tilegrid.pixel_shader[old_cursor_pos[0], old_cursor_pos[1]]} new: {terminal_tilegrid.pixel_shader[cursor.col, cursor.row]}")
|
||||
|
||||
# visible_cursor.anchored_position = ((cursor.col * 6) - 1, (cursor.row * 12) + 20)
|
||||
|
||||
# visible_cursor.anchored_position = ((cursor.col * 6), ((cursor.row - window.row) * 12))
|
||||
#
|
||||
# try:
|
||||
# visible_cursor.text = buffer.lines[cursor.row][cursor.col]
|
||||
# except IndexError:
|
||||
# visible_cursor.text = " "
|
||||
|
||||
|
||||
old_cursor_pos = (cursor.col, cursor.row)
|
||||
old_window_pos = (window.col, window.row)
|
||||
|
||||
def edit(filename, terminal=None, mouse=None, terminal_tilegrid=None):
|
||||
with MaybeDisableReload():
|
||||
if terminal is None:
|
||||
return curses.wrapper(editor, filename)
|
||||
else:
|
||||
return curses.custom_terminal_wrapper(terminal, editor, filename, mouse, terminal_tilegrid)
|
||||
194
builtin_apps/editor/adafruit_editor/picker.py
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
import usb_cdc
|
||||
from . import dang as curses
|
||||
from . import util
|
||||
|
||||
# pylint: disable=redefined-builtin
|
||||
|
||||
# def print(message):
|
||||
# usb_cdc.data.write(f"{message}\r\n".encode("utf-8"))
|
||||
|
||||
|
||||
always = ["code.py", "boot.py", "settings.toml", "boot_out.txt"]
|
||||
good_extensions = [".py", ".toml", ".txt", ".json"]
|
||||
|
||||
|
||||
def os_exists(filename):
|
||||
try:
|
||||
os.stat(filename)
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def isdir(filename):
|
||||
return os.stat(filename)[0] & 0o40_000
|
||||
|
||||
|
||||
def has_good_extension(filename):
|
||||
for g in good_extensions:
|
||||
if filename.endswith(g):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def picker(stdscr, options, notes=(), start_idx=0):
|
||||
stdscr.erase()
|
||||
visible_files = None
|
||||
if len(options) > curses.LINES - 1:
|
||||
visible_files = options[:curses.LINES - 1]
|
||||
else:
|
||||
visible_files = options
|
||||
|
||||
scroll_offset = 0
|
||||
need_to_scroll = False
|
||||
|
||||
# del options[curses.LINES - 1:]
|
||||
print(f"len opts: {len(options)}")
|
||||
print(f"len vis: {len(visible_files)}")
|
||||
|
||||
def _draw_file_list():
|
||||
|
||||
for row, option in enumerate(visible_files):
|
||||
if row < len(notes) and (note := notes[row]):
|
||||
option = f"{option} {note}"
|
||||
try:
|
||||
space_count = max(len(visible_files[row + 1]), len(visible_files[row - 1])) - len(option)
|
||||
if space_count < 0:
|
||||
space_count = 0
|
||||
except IndexError:
|
||||
space_count = curses.COLS - len(option)
|
||||
stdscr.addstr(row, 3, option + " " * space_count)
|
||||
stdscr.addstr(curses.LINES - 1, 0, "Enter: select | ^C: quit | ^N: New")
|
||||
|
||||
_draw_file_list()
|
||||
|
||||
old_idx = None
|
||||
idx = start_idx
|
||||
while True:
|
||||
|
||||
if need_to_scroll:
|
||||
need_to_scroll = False
|
||||
_draw_file_list()
|
||||
|
||||
if idx != old_idx:
|
||||
if old_idx is not None:
|
||||
stdscr.addstr(old_idx, 0, " ")
|
||||
stdscr.addstr(idx, 0, "=>")
|
||||
old_idx = idx
|
||||
|
||||
k = stdscr.getkey()
|
||||
|
||||
if k == "KEY_DOWN":
|
||||
print(f"{scroll_offset + len(visible_files)} < {len(options)}")
|
||||
if scroll_offset + len(visible_files) < len(options):
|
||||
if idx == len(visible_files) - 1:
|
||||
need_to_scroll = True
|
||||
scroll_offset += 1
|
||||
visible_files = options[scroll_offset:scroll_offset + curses.LINES - 1]
|
||||
idx = min(idx + 1, len(visible_files) - 1)
|
||||
|
||||
elif k == "KEY_UP":
|
||||
if scroll_offset > 0:
|
||||
if idx == 0:
|
||||
need_to_scroll = True
|
||||
scroll_offset -= 1
|
||||
visible_files = options[scroll_offset:scroll_offset + curses.LINES - 1]
|
||||
idx = max(idx - 1, 0)
|
||||
elif k == "\n":
|
||||
if visible_files[idx] == "../":
|
||||
os.chdir("../")
|
||||
options, notes = _files_list()
|
||||
return picker(stdscr, options, notes)
|
||||
elif isdir(visible_files[idx]):
|
||||
os.chdir(visible_files[idx])
|
||||
options, notes = _files_list()
|
||||
return picker(stdscr, options, notes)
|
||||
else:
|
||||
return visible_files[idx]
|
||||
|
||||
|
||||
# ctrl-N
|
||||
elif k == "\x0E":
|
||||
# if not util.readonly():
|
||||
new_file_name = new_file(stdscr)
|
||||
if new_file_name is not None:
|
||||
return new_file_name
|
||||
else:
|
||||
time.sleep(2)
|
||||
stdscr.erase()
|
||||
old_idx = None
|
||||
_draw_file_list()
|
||||
|
||||
|
||||
|
||||
def terminal_input(stdscr, message):
|
||||
stdscr.erase()
|
||||
stdscr.addstr(0, 0, message)
|
||||
input_str_list = []
|
||||
k = stdscr.getkey()
|
||||
while k != "\n":
|
||||
if k is not None:
|
||||
if len(k) == 1 and " " <= k <= "~":
|
||||
input_str_list.append(k)
|
||||
stdscr.addstr(0, len(message) + len(input_str_list) - 1, k)
|
||||
elif k == "\x08":
|
||||
input_str_list.pop(len(input_str_list) - 1)
|
||||
stdscr.addstr(0, len(message) + len(input_str_list) - 1, k)
|
||||
k = stdscr.getkey()
|
||||
# submit after enter pressed
|
||||
return "".join(input_str_list)
|
||||
|
||||
|
||||
# pylint: disable=inconsistent-return-statements
|
||||
def new_file(stdscr):
|
||||
stdscr.erase()
|
||||
print(f"cwd inside new_file(): {os.getcwd()}")
|
||||
new_file_name = terminal_input(stdscr, "New File Name: ")
|
||||
if os_exists(new_file_name):
|
||||
stdscr.addstr(1,0, "Error: File Already Exists")
|
||||
return
|
||||
print(f"new filename: {new_file_name}")
|
||||
if not new_file_name.startswith("/saves/") and not new_file_name.startswith("/sd/"):
|
||||
if not util.readonly():
|
||||
with open(new_file_name, "w") as f:
|
||||
f.write("")
|
||||
|
||||
return new_file_name
|
||||
else:
|
||||
stdscr.addstr(1, 0, "Error: Cannot create file in readonly storage")
|
||||
else:
|
||||
with open(new_file_name, "w") as f:
|
||||
f.write("")
|
||||
return new_file_name
|
||||
|
||||
|
||||
def _files_list():
|
||||
options = sorted(
|
||||
(
|
||||
g
|
||||
for g in os.listdir(".")
|
||||
if not g.startswith(".")
|
||||
),
|
||||
key=lambda filename: (not has_good_extension(filename), filename),
|
||||
) # + always[:]
|
||||
|
||||
if os.getcwd() != "/":
|
||||
options.insert(0, "../")
|
||||
|
||||
# notes = [None if os_exists(filename) else "(NEW)" for filename in options]
|
||||
notes = [None] * len(options)
|
||||
return options, notes
|
||||
|
||||
|
||||
def pick_file(terminal):
|
||||
os.chdir("/")
|
||||
options, notes = _files_list()
|
||||
return curses.custom_terminal_wrapper(terminal, picker, options, notes)
|
||||
10
builtin_apps/editor/adafruit_editor/util.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
def readonly():
|
||||
try:
|
||||
import storage # pylint: disable=import-outside-toplevel
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
return storage.getmount("/").readonly
|
||||
88
builtin_apps/editor/code.py
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import atexit
|
||||
import os
|
||||
|
||||
import supervisor
|
||||
from displayio import Group, Palette, TileGrid
|
||||
from adafruit_display_text.bitmap_label import Label
|
||||
from adafruit_editor import editor, picker
|
||||
from tilepalettemapper import TilePaletteMapper
|
||||
from adafruit_argv_file import read_argv, write_argv
|
||||
from adafruit_fruitjam.peripherals import request_display_config
|
||||
from adafruit_usb_host_mouse import find_and_init_boot_mouse
|
||||
import terminalio
|
||||
import usb
|
||||
|
||||
print(f"cwd in editor/code.py: {os.getcwd()}")
|
||||
|
||||
request_display_config(720, 400)
|
||||
display = supervisor.runtime.display
|
||||
display.auto_refresh = True
|
||||
|
||||
main_group = Group()
|
||||
|
||||
display.root_group = main_group
|
||||
|
||||
font_palette = Palette(2)
|
||||
font_palette[0] = 0x000000
|
||||
font_palette[1] = 0xFFFFFF
|
||||
|
||||
font = terminalio.FONT
|
||||
char_size = font.get_bounding_box()
|
||||
screen_size = (display.width // char_size[0], display.height // char_size[1])
|
||||
print(screen_size)
|
||||
|
||||
highlight_palette = Palette(3)
|
||||
highlight_palette[0] = 0x000000
|
||||
highlight_palette[1] = 0xFFFFFF
|
||||
highlight_palette[2] = 0xC9C9C9
|
||||
|
||||
|
||||
tpm = TilePaletteMapper(highlight_palette, 2)
|
||||
terminal_area = TileGrid(bitmap=font.bitmap, width=screen_size[0], height=screen_size[1],
|
||||
tile_width=char_size[0], tile_height=char_size[1], pixel_shader=tpm)
|
||||
|
||||
for x in range(screen_size[0]):
|
||||
tpm[x,screen_size[1]-1] = [2,0]
|
||||
|
||||
main_group.append(terminal_area)
|
||||
|
||||
terminal = terminalio.Terminal(terminal_area, font)
|
||||
|
||||
# visible_cursor = Label(terminalio.FONT, text="",
|
||||
# color=0x000000, background_color=0xeeeeee, padding_left=1)
|
||||
# visible_cursor.hidden = False
|
||||
# visible_cursor.anchor_point = (0, 0)
|
||||
# visible_cursor.anchored_position = (0, 0)
|
||||
# main_group.append(visible_cursor)
|
||||
|
||||
file = None
|
||||
args = read_argv(__file__)
|
||||
if args is not None and len(args) > 0:
|
||||
file = args[0]
|
||||
else:
|
||||
file = picker.pick_file(terminal)
|
||||
|
||||
usb_device_count = 0
|
||||
for dev in usb.core.find(find_all=True):
|
||||
usb_device_count += 1
|
||||
|
||||
mouse = None
|
||||
if usb_device_count > 1:
|
||||
mouse = find_and_init_boot_mouse()
|
||||
|
||||
if mouse is not None:
|
||||
mouse.x = display.width - 6
|
||||
main_group.append(mouse.tilegrid)
|
||||
|
||||
|
||||
def atexit_callback():
|
||||
"""
|
||||
re-attach USB devices to kernel if needed.
|
||||
:return:
|
||||
"""
|
||||
print("inside atexit callback")
|
||||
if mouse is not None:
|
||||
mouse.release()
|
||||
|
||||
atexit.register(atexit_callback)
|
||||
editor.edit(file, terminal, mouse, terminal_area)
|
||||
BIN
builtin_apps/editor/icon.bmp
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
4
builtin_apps/editor/metadata.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"title": "Editor",
|
||||
"icon": "icon.bmp"
|
||||
}
|
||||
0
learn-projects/.gitkeep
Normal file
4
mock_boot_out.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
Adafruit CircuitPython 10.0.0-beta.0 on 2025-07-15; Adafruit Fruit Jam with rp2350b
|
||||
Board ID:adafruit_fruit_jam
|
||||
UID:
|
||||
boot.py output:
|
||||
196
release_updater.py
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import hashlib
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from urllib.request import urlretrieve
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
import requests
|
||||
|
||||
from build import main as build_main
|
||||
|
||||
|
||||
def get_file_sha256(file_path):
|
||||
"""Calculates the SHA256 hash of a file.
|
||||
|
||||
Args:
|
||||
file_path: The path to the file.
|
||||
|
||||
Returns:
|
||||
The SHA256 hash of the file as a hexadecimal string.
|
||||
"""
|
||||
sha256_hash = hashlib.sha256()
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(4096), b""):
|
||||
sha256_hash.update(chunk)
|
||||
except FileNotFoundError:
|
||||
return "File not found."
|
||||
return sha256_hash.hexdigest()
|
||||
|
||||
def print_hashes(hash_dict):
|
||||
for filename in sorted(hash_dict.keys()):
|
||||
print(f"{filename}: {hash_dict[filename]}")
|
||||
|
||||
|
||||
def is_release_required():
|
||||
"""
|
||||
Checks if there are new versions of any apps. When new versions exist
|
||||
a new release is required.
|
||||
|
||||
:return: True if there are new versions of any apps, and release is required, False otherwise.
|
||||
"""
|
||||
|
||||
try:
|
||||
dl_dir = Path("latest_dl")
|
||||
if dl_dir.exists():
|
||||
shutil.rmtree("latest_dl")
|
||||
dl_dir.mkdir()
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
# download built zips from most recent release
|
||||
RELEASE_API_URL = "https://api.github.com/repos/adafruit/Fruit-Jam-OS/releases?per_page=1"
|
||||
latest_release_obj = requests.get(RELEASE_API_URL).json()[0]
|
||||
for asset in latest_release_obj["assets"]:
|
||||
asset_dl_url = asset["browser_download_url"]
|
||||
asset_filename = asset_dl_url.split("/")[-1]
|
||||
urlretrieve(asset_dl_url, f"latest_dl/{asset_filename}")
|
||||
|
||||
# get sha256 hashes for each downloaded zip
|
||||
downloaded_file_hashes = {}
|
||||
for dl_file in Path("latest_dl").iterdir():
|
||||
downloaded_file_hashes[dl_file.name] = get_file_sha256(dl_file)
|
||||
|
||||
# make a local build
|
||||
build_main()
|
||||
|
||||
# get sha256 hashes for built zips
|
||||
dist_file_hashes = {}
|
||||
for dist_file in Path("dist").iterdir():
|
||||
dist_file_hashes[dist_file.name] = get_file_sha256(dist_file)
|
||||
|
||||
print("Downloaded file hashes:")
|
||||
print_hashes(downloaded_file_hashes)
|
||||
|
||||
print("Dist file hashes:")
|
||||
print_hashes(dist_file_hashes)
|
||||
|
||||
# compare hashes
|
||||
if dist_file_hashes != downloaded_file_hashes:
|
||||
print("Zip hashes differ, a release is required.")
|
||||
return True
|
||||
|
||||
print("Zip hashes match, no release required.")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def parse_semantic_version(version_string):
|
||||
"""Parse semantic version string and return (major, minor, patch)."""
|
||||
|
||||
# Match semantic version pattern
|
||||
match = re.match(r'^(\d+)\.(\d+)\.(\d+)(?:-.*)?(?:\+.*)?$', version_string)
|
||||
if not match:
|
||||
raise ValueError(f"Invalid semantic version: {version_string}")
|
||||
|
||||
return int(match.group(1)), int(match.group(2)), int(match.group(3))
|
||||
|
||||
|
||||
def increment_patch_version(version_string):
|
||||
"""Increment patch version by 1."""
|
||||
major, minor, patch = parse_semantic_version(version_string)
|
||||
new_patch = patch + 1
|
||||
return f"{major}.{minor}.{new_patch}"
|
||||
|
||||
|
||||
def get_latest_release():
|
||||
"""Fetch the latest release from GitHub API."""
|
||||
url = f"https://api.github.com/repos/adafruit/Fruit-Jam-OS/releases/latest"
|
||||
headers = {
|
||||
"Authorization": f"token {os.getenv('GITHUB_TOKEN')}",
|
||||
"Accept": "application/vnd.github.v3+json"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
|
||||
if response.status_code != 200:
|
||||
print(f"Error fetching latest release: {response.status_code}")
|
||||
print(f"Response: {response.text}")
|
||||
sys.exit(1)
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
def create_release(tag_name):
|
||||
"""Create a new GitHub release."""
|
||||
url = f"https://api.github.com/repos/adafruit/Fruit-Jam-OS/releases"
|
||||
headers = {
|
||||
"Authorization": f"token {os.getenv('GITHUB_TOKEN')}",
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
data = {
|
||||
"tag_name": tag_name,
|
||||
"name": tag_name,
|
||||
"body": f"Release {tag_name}",
|
||||
"draft": False,
|
||||
"prerelease": False
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, data=json.dumps(data))
|
||||
|
||||
if response.status_code != 201:
|
||||
print(f"Error creating release: {response.status_code}")
|
||||
print(f"Response: {response.text}")
|
||||
sys.exit(1)
|
||||
|
||||
return response.json()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if is_release_required():
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "make_release":
|
||||
print(f"Creating release for Fruit Jam OS")
|
||||
|
||||
# Get latest release
|
||||
latest_release = get_latest_release()
|
||||
|
||||
if latest_release:
|
||||
latest_tag = latest_release["tag_name"]
|
||||
print(f"Latest release: {latest_tag}")
|
||||
|
||||
try:
|
||||
new_tag = increment_patch_version(latest_tag)
|
||||
except ValueError as e:
|
||||
print(f"Error parsing version: {e}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
new_tag = "0.1.0"
|
||||
|
||||
print(f"Creating new release: {new_tag}")
|
||||
|
||||
new_release = create_release(
|
||||
tag_name=new_tag,
|
||||
)
|
||||
|
||||
#print(new_release)
|
||||
|
||||
github_output_path = os.environ.get('GITHUB_OUTPUT')
|
||||
|
||||
if github_output_path:
|
||||
with open(github_output_path, 'a') as f:
|
||||
f.write(f"release_created=true\n")
|
||||
f.write(f"assets_upload_url={new_release['upload_url']}\n")
|
||||
|
||||
print(f"Successfully created release: {new_tag}")
|
||||
print(f"Release URL: {new_release['html_url']}")
|
||||
else:
|
||||
github_output_path = os.environ.get('GITHUB_OUTPUT')
|
||||
|
||||
if github_output_path:
|
||||
with open(github_output_path, 'a') as f:
|
||||
f.write(f"release_created=false\n")
|
||||
f.write(f"assets_upload_url=None\n")
|
||||
2
requirements.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
circup
|
||||
requests
|
||||
0
src/__init__.py
Normal file
686
src/boot.py
|
|
@ -1,673 +1,47 @@
|
|||
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
import gc
|
||||
|
||||
import board
|
||||
import displayio
|
||||
import supervisor
|
||||
from displayio import OnDiskBitmap, TileGrid, Group
|
||||
import adafruit_imageload
|
||||
import time
|
||||
import math
|
||||
import adafruit_tlv320
|
||||
from audiocore import WaveFile
|
||||
import audiobusio
|
||||
from adafruit_argv_file import read_argv, write_argv
|
||||
import storage
|
||||
|
||||
BOX_SIZE = (235, 107)
|
||||
TARGET_FPS = 70
|
||||
supervisor.runtime.autoreload = False
|
||||
|
||||
display = supervisor.runtime.display
|
||||
display.auto_refresh = False
|
||||
"""
|
||||
boot.py arguments
|
||||
|
||||
i2c = board.I2C()
|
||||
dac = adafruit_tlv320.TLV320DAC3100(i2c)
|
||||
0: storage readonly flag, False means writable to CircuitPython, True means read-only to CircuitPython
|
||||
1: next code files
|
||||
2-N: args to pass to next code file
|
||||
|
||||
# set sample rate & bit depth
|
||||
dac.configure_clocks(sample_rate=11030, bit_depth=16)
|
||||
|
||||
# use headphones
|
||||
dac.headphone_output = True
|
||||
dac.headphone_volume = -15 # dB
|
||||
|
||||
wave_file = open("/boot_animation/ada_fruitjam_boot_jingle.wav", "rb")
|
||||
wave = WaveFile(wave_file)
|
||||
audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)
|
||||
"""
|
||||
|
||||
|
||||
class OvershootAnimator:
|
||||
"""
|
||||
A non-blocking animator that moves an element to a target with overshoot effect.
|
||||
args = read_argv(__file__)
|
||||
if args is not None and len(args) > 0:
|
||||
|
||||
Instead of blocking with sleep(), this class provides a tick() method that
|
||||
should be called repeatedly by an external loop (e.g., game loop, UI event loop).
|
||||
"""
|
||||
readonly = args[0]
|
||||
next_code_file = None
|
||||
remaining_args = None
|
||||
|
||||
def __init__(self, element):
|
||||
"""
|
||||
Initialize the animator with an element to animate.
|
||||
if len(args) >= 1:
|
||||
next_code_file = args[1]
|
||||
if len(args) >= 2:
|
||||
remaining_args = args[2:]
|
||||
|
||||
Parameters:
|
||||
- element: An object with x and y properties that will be animated
|
||||
"""
|
||||
self.element = element
|
||||
self.pos_animating = False
|
||||
self.start_time = 0
|
||||
self.start_x = 0
|
||||
self.start_y = 0
|
||||
self.target_x = 0
|
||||
self.target_y = 0
|
||||
self.overshoot_x = 0
|
||||
self.overshoot_y = 0
|
||||
self.duration = 0
|
||||
self.overshoot_pixels = 0
|
||||
self.eased_value = None
|
||||
if remaining_args is not None:
|
||||
write_argv(next_code_file, remaining_args)
|
||||
|
||||
self.cur_sprite_index = None
|
||||
self.last_sprite_frame_time = -1
|
||||
self.sprite_anim_start_time = -1
|
||||
self.sprite_anim_from_index = None
|
||||
self.sprite_anim_to_index = None
|
||||
self.sprite_anim_delay = None
|
||||
# print(f"setting storage readonly to: {readonly}")
|
||||
storage.remount("/", readonly=readonly)
|
||||
|
||||
def animate_to(self, target_x, target_y, duration=1.0, overshoot_pixels=20,
|
||||
start_sprite_anim_at=None, sprite_delay=1 / 60,
|
||||
sprite_from_index=None, sprite_to_index=None, eased_value=None):
|
||||
next_code_file = next_code_file
|
||||
supervisor.set_next_code_file(next_code_file, sticky_on_reload=False, reload_on_error=True,
|
||||
working_directory="/".join(next_code_file.split("/")[:-1]))
|
||||
|
||||
"""
|
||||
Start a new animation to the specified target.
|
||||
|
||||
Parameters:
|
||||
- target_x, target_y: The final target coordinates
|
||||
- duration: Total animation time in seconds
|
||||
- overshoot_pixels: How many pixels to overshoot beyond the target
|
||||
(use 0 for no overshoot)
|
||||
"""
|
||||
_now = time.monotonic()
|
||||
|
||||
# Record starting position and time
|
||||
self.start_x = self.element.x
|
||||
self.start_y = self.element.y
|
||||
self.start_time = _now
|
||||
if start_sprite_anim_at is not None:
|
||||
self.sprite_anim_start_time = _now + start_sprite_anim_at
|
||||
self.sprite_anim_to_index = sprite_to_index
|
||||
self.sprite_anim_from_index = sprite_from_index
|
||||
self.cur_sprite_index = self.sprite_anim_from_index
|
||||
self.sprite_anim_delay = sprite_delay
|
||||
|
||||
# Store target position and parameters
|
||||
self.target_x = target_x
|
||||
self.target_y = target_y
|
||||
self.duration = duration
|
||||
self.overshoot_pixels = overshoot_pixels
|
||||
|
||||
# Calculate distance to target
|
||||
dx = target_x - self.start_x
|
||||
dy = target_y - self.start_y
|
||||
|
||||
# Calculate the direction vector (normalized)
|
||||
distance = math.sqrt(dx * dx + dy * dy)
|
||||
if distance <= 0:
|
||||
# Already at target
|
||||
return False
|
||||
|
||||
dir_x = dx / distance
|
||||
dir_y = dy / distance
|
||||
|
||||
# Calculate overshoot position
|
||||
self.overshoot_x = target_x + dir_x * overshoot_pixels
|
||||
self.overshoot_y = target_y + dir_y * overshoot_pixels
|
||||
|
||||
self.eased_value = eased_value
|
||||
|
||||
# Start the animation
|
||||
self.pos_animating = True
|
||||
return True
|
||||
|
||||
def sprite_anim_tick(self, cur_time):
|
||||
if cur_time >= self.last_sprite_frame_time + self.sprite_anim_delay:
|
||||
self.element[0] = self.cur_sprite_index
|
||||
self.last_sprite_frame_time = cur_time
|
||||
self.cur_sprite_index += 1
|
||||
|
||||
if self.cur_sprite_index > self.sprite_anim_to_index:
|
||||
self.cur_sprite_index = None
|
||||
self.sprite_anim_from_index = None
|
||||
self.sprite_anim_to_index = None
|
||||
self.sprite_anim_delay = None
|
||||
self.last_sprite_frame_time = -1
|
||||
self.sprite_anim_start_time = -1
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def tick(self):
|
||||
"""
|
||||
Update the animation based on the current time.
|
||||
|
||||
This method should be called repeatedly until it returns False.
|
||||
|
||||
Returns:
|
||||
- True if the animation is still in progress
|
||||
- False if the animation has completed
|
||||
"""
|
||||
still_sprite_animating = False
|
||||
_now = time.monotonic()
|
||||
if self.cur_sprite_index is not None:
|
||||
if _now >= self.sprite_anim_start_time:
|
||||
still_sprite_animating = self.sprite_anim_tick(_now)
|
||||
# print("sprite_still_animating", still_sprite_animating)
|
||||
if not still_sprite_animating:
|
||||
return False
|
||||
else:
|
||||
# skip boot animation if no display
|
||||
if supervisor.runtime.display is None:
|
||||
supervisor.set_next_code_file("code.py")
|
||||
else:
|
||||
if not self.pos_animating:
|
||||
# print("returning false cur_sprite_index was None and pos_animating False")
|
||||
return False
|
||||
|
||||
# Calculate elapsed time and progress
|
||||
elapsed = _now - self.start_time
|
||||
progress = elapsed / self.duration
|
||||
|
||||
# Check if animation is complete
|
||||
if progress >= 1.0:
|
||||
# Ensure we end exactly at the target
|
||||
if self.element.x != self.target_x or self.element.y != self.target_y:
|
||||
self.element.x = self.target_x
|
||||
self.element.y = self.target_y
|
||||
|
||||
self.pos_animating = False
|
||||
if still_sprite_animating:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# Calculate the current position based on progress
|
||||
if self.overshoot_pixels > 0:
|
||||
# Two-phase animation with overshoot
|
||||
if progress < 0.7: # Move smoothly toward overshoot position
|
||||
# Use a single smooth curve to the overshoot point
|
||||
eased = progress / 0.7 # Linear acceleration toward overshoot
|
||||
# Apply slight ease-in to make it accelerate through the target point
|
||||
eased = eased ** 1.2
|
||||
current_x = self.start_x + (self.overshoot_x - self.start_x) * eased
|
||||
current_y = self.start_y + (self.overshoot_y - self.start_y) * eased
|
||||
else: # Return from overshoot to target
|
||||
sub_progress = (progress - 0.7) / 0.3
|
||||
# Decelerate toward final target
|
||||
eased = 1 - (1 - sub_progress) ** 2 # ease-out quad
|
||||
current_x = self.overshoot_x + (self.target_x - self.overshoot_x) * eased
|
||||
current_y = self.overshoot_y + (self.target_y - self.overshoot_y) * eased
|
||||
else:
|
||||
# Simple ease-out when no overshoot is desired
|
||||
if self.eased_value is None:
|
||||
eased = 1 - (1 - progress) ** 4
|
||||
else:
|
||||
eased = progress / self.eased_value
|
||||
current_x = self.start_x + (self.target_x - self.start_x) * eased
|
||||
current_y = self.start_y + (self.target_y - self.start_y) * eased
|
||||
|
||||
# Update element position
|
||||
self.element.x = int(current_x)
|
||||
self.element.y = int(current_y)
|
||||
|
||||
return True
|
||||
|
||||
def is_animating(self):
|
||||
"""Check if an animation is currently in progress."""
|
||||
return self.pos_animating
|
||||
|
||||
def cancel(self):
|
||||
"""Cancel the current animation."""
|
||||
self.pos_animating = False
|
||||
|
||||
|
||||
apple_sprites, apple_sprites_palette = adafruit_imageload.load("/boot_animation/apple_spritesheet.bmp")
|
||||
f_sprites, f_sprites_palette = adafruit_imageload.load("/boot_animation/f_spritesheet.bmp")
|
||||
r_sprites, r_sprites_palette = adafruit_imageload.load("/boot_animation/r_spritesheet.bmp")
|
||||
u_sprites, u_sprites_palette = adafruit_imageload.load("/boot_animation/u_spritesheet.bmp")
|
||||
i_sprites, i_sprites_palette = adafruit_imageload.load("/boot_animation/i_spritesheet.bmp")
|
||||
t_sprites, t_sprites_palette = adafruit_imageload.load("/boot_animation/t_spritesheet.bmp")
|
||||
j_sprites, j_sprites_palette = adafruit_imageload.load("/boot_animation/j_spritesheet.bmp")
|
||||
j_sprites_palette.make_transparent(0)
|
||||
a_sprites, a_sprites_palette = adafruit_imageload.load("/boot_animation/a_spritesheet.bmp")
|
||||
a_sprites_palette.make_transparent(0)
|
||||
m_sprites, m_sprites_palette = adafruit_imageload.load("/boot_animation/m_spritesheet.bmp")
|
||||
m_sprites_palette.make_transparent(0)
|
||||
|
||||
default_sprite_delay = 1 / 35
|
||||
|
||||
main_group = Group()
|
||||
main_group.x = display.width // 2 - BOX_SIZE[0] // 2 - 30
|
||||
main_group.y = display.height // 2 - BOX_SIZE[1] // 2 - 31
|
||||
|
||||
sliding_group = Group()
|
||||
main_group.append(sliding_group)
|
||||
|
||||
letters_x_start = 83
|
||||
letters_y_start = display.height
|
||||
|
||||
apple_tilegrid = TileGrid(apple_sprites, pixel_shader=apple_sprites_palette,
|
||||
tile_width=73, tile_height=107, width=1, height=1)
|
||||
f_tilegrid = TileGrid(f_sprites, pixel_shader=f_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
r_tilegrid = TileGrid(r_sprites, pixel_shader=r_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
u_tilegrid = TileGrid(u_sprites, pixel_shader=u_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
i_tilegrid = TileGrid(i_sprites, pixel_shader=i_sprites_palette,
|
||||
tile_width=16, tile_height=39, width=1, height=1)
|
||||
t_tilegrid = TileGrid(t_sprites, pixel_shader=t_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
j_tilegrid = TileGrid(j_sprites, pixel_shader=j_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
a_tilegrid = TileGrid(a_sprites, pixel_shader=a_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
m_tilegrid = TileGrid(m_sprites, pixel_shader=m_sprites_palette,
|
||||
tile_width=43, tile_height=39, width=1, height=1)
|
||||
|
||||
coordinator = {
|
||||
"steps": [
|
||||
# Apple fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": apple_tilegrid,
|
||||
"offscreen_loc": (0, -207),
|
||||
"onscreen_loc": (0, 21),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 1,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 11),
|
||||
"sprite_delay": 1 / 42,
|
||||
"start_time": 0.0,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
},
|
||||
# F fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": f_tilegrid,
|
||||
"offscreen_loc": (letters_x_start, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start, 67),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 20,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 0.45,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
|
||||
},
|
||||
# R fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": r_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + 32 + 3 - 1, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + 32 + 3 - 1, 67),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 20,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 0.9,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
},
|
||||
# Left slide everything
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": sliding_group,
|
||||
"offscreen_loc": (100, 0),
|
||||
"onscreen_loc": (30, 0),
|
||||
"move_duration": 1.75,
|
||||
"overshoot_pixels": 0,
|
||||
"eased_value": 1,
|
||||
"sprite_anim_range": None,
|
||||
"sprite_delay": None,
|
||||
"start_time": 0.9,
|
||||
"sprite_anim_start": None,
|
||||
"started": False,
|
||||
},
|
||||
# U fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": u_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 2 - 2, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 2 - 2, 67),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 20,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 1.35,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
},
|
||||
# I fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": i_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 3 - 3, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 3 - 3, 67),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 20,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 1.8,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
},
|
||||
# T fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": t_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 3 + 16 + 3 - 4, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 3 + 16 + 3 - 4, 67),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 20,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 2.25,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
},
|
||||
# J fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": j_tilegrid,
|
||||
"offscreen_loc": (letters_x_start, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start, 50 + 39),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 4,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 2.7,
|
||||
# "sprite_anim_start": 0.347,
|
||||
"sprite_anim_start": 0.4,
|
||||
"started": False,
|
||||
},
|
||||
# A fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": a_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + 32 + 3 - 1, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + 32 + 3 - 1, 50 + 39),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 4,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 3.15,
|
||||
"sprite_anim_start": 0.4,
|
||||
"started": False,
|
||||
},
|
||||
# M fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": m_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + 32 + 3 + 32 + 2 - 1, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + 32 + 3 + 32 + 2 - 1, 50 + 39),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 4,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 3.6,
|
||||
"sprite_anim_start": 0.4,
|
||||
"started": False,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
for step in coordinator["steps"]:
|
||||
if isinstance(step["tilegrid"], TileGrid):
|
||||
sliding_group.append(step["tilegrid"])
|
||||
step["default_palette"] = step["tilegrid"].pixel_shader
|
||||
step["tilegrid"].x = step["offscreen_loc"][0]
|
||||
step["tilegrid"].y = step["offscreen_loc"][1]
|
||||
step["animator"] = OvershootAnimator(step["tilegrid"])
|
||||
|
||||
# F bounce up from J impact
|
||||
coordinator["steps"].insert(8,
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][1]["tilegrid"],
|
||||
"animator": coordinator["steps"][1]["animator"],
|
||||
"offscreen_loc": (letters_x_start, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start, 52),
|
||||
"move_duration": 0.3,
|
||||
"overshoot_pixels": 22,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (19, 27),
|
||||
"sprite_delay": 1 / 22,
|
||||
"start_time": 3.0,
|
||||
"sprite_anim_start": 0.15,
|
||||
"started": False,
|
||||
},
|
||||
)
|
||||
# R bounce up from A impact
|
||||
coordinator["steps"].insert(10,
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][2]["tilegrid"],
|
||||
"animator": coordinator["steps"][2]["animator"],
|
||||
"offscreen_loc": (letters_x_start + 32 + 3 - 1, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + 32 + 3 - 1, 52),
|
||||
"move_duration": 0.3,
|
||||
"overshoot_pixels": 22,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (19, 27),
|
||||
"sprite_delay": 1 / 22,
|
||||
"start_time": 3.45,
|
||||
"sprite_anim_start": 0.15,
|
||||
"started": False,
|
||||
},
|
||||
)
|
||||
# U bounce up from M impact
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][4]["tilegrid"],
|
||||
"animator": coordinator["steps"][4]["animator"],
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 2 - 2, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 2 - 2, 52),
|
||||
"move_duration": 0.3,
|
||||
"overshoot_pixels": 22,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (19, 27),
|
||||
"sprite_delay": 1 / 22,
|
||||
"start_time": 3.9,
|
||||
"sprite_anim_start": 0.15,
|
||||
"started": False,
|
||||
},
|
||||
)
|
||||
# I bounce up from M impact
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][5]["tilegrid"],
|
||||
"animator": coordinator["steps"][5]["animator"],
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 3 - 3, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 3 - 3, 52),
|
||||
"move_duration": 0.3,
|
||||
"overshoot_pixels": 22,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (19, 27),
|
||||
"sprite_delay": 1 / 22,
|
||||
"start_time": 4.00,
|
||||
"sprite_anim_start": 0.15,
|
||||
"started": False,
|
||||
},
|
||||
)
|
||||
# T bounce up from M impact
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][6]["tilegrid"],
|
||||
"animator": coordinator["steps"][6]["animator"],
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 3 + 16 + 3 - 4, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 3 + 16 + 3 - 4, 52),
|
||||
"move_duration": 0.3,
|
||||
"overshoot_pixels": 22,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (19, 27),
|
||||
"sprite_delay": 1 / 22,
|
||||
"start_time": 4.1,
|
||||
"sprite_anim_start": 0.15,
|
||||
"started": False,
|
||||
},
|
||||
)
|
||||
# color red
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 4.75,
|
||||
"type": "change_palette",
|
||||
"new_palette": "red_palette",
|
||||
"color": 0xff0000,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# color yellow
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 5,
|
||||
"type": "change_palette",
|
||||
"new_palette": "yellow_palette",
|
||||
"color": 0xffff00,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# color teal
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 5.25,
|
||||
"type": "change_palette",
|
||||
"new_palette": "teal_palette",
|
||||
"color": 0x00ffff,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# color pink
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 5.5,
|
||||
"type": "change_palette",
|
||||
"new_palette": "pink_palette",
|
||||
"color": 0xff00ff,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# color blue
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 5.75,
|
||||
"type": "change_palette",
|
||||
"new_palette": "blue_palette",
|
||||
"color": 0x0000ff,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# color green
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 6.00,
|
||||
"type": "change_palette",
|
||||
"new_palette": "green_palette",
|
||||
"color": 0x00ff00,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# Apple eyes blink
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][0]["tilegrid"],
|
||||
"animator": coordinator["steps"][0]["animator"],
|
||||
"offscreen_loc": (0, -207),
|
||||
"onscreen_loc": (0, 21),
|
||||
"move_duration": 0.01,
|
||||
"overshoot_pixels": 0,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (12, 27),
|
||||
"sprite_delay": 1 / 32,
|
||||
"start_time": 6.65,
|
||||
"sprite_anim_start": 0.0,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# Apple eyes blink again
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][0]["tilegrid"],
|
||||
"animator": coordinator["steps"][0]["animator"],
|
||||
"offscreen_loc": (0, -207),
|
||||
"onscreen_loc": (0, 21),
|
||||
"move_duration": 0.01,
|
||||
"overshoot_pixels": 0,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (12, 18),
|
||||
"sprite_delay": 1 / 32,
|
||||
"start_time": 8.75,
|
||||
"sprite_anim_start": 0.0,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
|
||||
display.root_group = main_group
|
||||
|
||||
start_time = time.monotonic()
|
||||
|
||||
audio.play(wave)
|
||||
|
||||
while True:
|
||||
now = time.monotonic()
|
||||
still_going = True
|
||||
|
||||
for i in range(len(coordinator["steps"])):
|
||||
step = coordinator["steps"][i]
|
||||
if now - start_time >= step["start_time"]:
|
||||
if not step["started"]:
|
||||
step["started"] = True
|
||||
if step["type"] == "animation_step":
|
||||
if step["sprite_anim_range"] is not None:
|
||||
step["animator"].animate_to(
|
||||
*step["onscreen_loc"],
|
||||
duration=step["move_duration"], overshoot_pixels=step["overshoot_pixels"],
|
||||
start_sprite_anim_at=step["sprite_anim_start"],
|
||||
sprite_from_index=step["sprite_anim_range"][0],
|
||||
sprite_to_index=step["sprite_anim_range"][1],
|
||||
sprite_delay=step["sprite_delay"], eased_value=step["eased_value"],
|
||||
)
|
||||
else:
|
||||
step["animator"].animate_to(
|
||||
*step["onscreen_loc"],
|
||||
duration=step["move_duration"], overshoot_pixels=step["overshoot_pixels"],
|
||||
eased_value=step["eased_value"]
|
||||
)
|
||||
elif step["type"] == "change_palette":
|
||||
# color_sweep_all(step["color"], delay=0)
|
||||
for _cur_step in coordinator["steps"]:
|
||||
if "tilegrid" in _cur_step and isinstance(_cur_step["tilegrid"], TileGrid):
|
||||
_cur_step["tilegrid"].pixel_shader[1] = step["color"]
|
||||
|
||||
if "animator" in step:
|
||||
if i == len(coordinator["steps"]) - 1:
|
||||
still_going = step["animator"].tick()
|
||||
else:
|
||||
step["animator"].tick()
|
||||
else:
|
||||
if i == len(coordinator["steps"]) - 1:
|
||||
still_going = False
|
||||
# display.refresh(target_frames_per_second=TARGET_FPS)
|
||||
display.refresh()
|
||||
|
||||
if not still_going:
|
||||
break
|
||||
|
||||
while audio.playing:
|
||||
pass
|
||||
supervisor.set_next_code_file("boot_animation.py")
|
||||
|
|
|
|||
704
src/boot_animation.py
Normal file
|
|
@ -0,0 +1,704 @@
|
|||
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
import gc
|
||||
import json
|
||||
|
||||
import board
|
||||
import supervisor
|
||||
from displayio import TileGrid, Group
|
||||
import adafruit_imageload
|
||||
import time
|
||||
import math
|
||||
import adafruit_tlv320
|
||||
from audiocore import WaveFile
|
||||
import audiobusio
|
||||
import adafruit_pathlib as pathlib
|
||||
|
||||
launcher_config = {}
|
||||
if pathlib.Path("launcher.conf.json").exists():
|
||||
with open("launcher.conf.json", "r") as f:
|
||||
launcher_config = json.load(f)
|
||||
|
||||
BOX_SIZE = (235, 107)
|
||||
TARGET_FPS = 70
|
||||
|
||||
display = supervisor.runtime.display
|
||||
display.auto_refresh = False
|
||||
|
||||
i2c = board.I2C()
|
||||
# Check if DAC is connected
|
||||
while not i2c.try_lock():
|
||||
time.sleep(0.01)
|
||||
if 0x18 in i2c.scan():
|
||||
ltv320_present = True
|
||||
else:
|
||||
ltv320_present = False
|
||||
i2c.unlock()
|
||||
|
||||
if ltv320_present:
|
||||
dac = adafruit_tlv320.TLV320DAC3100(i2c)
|
||||
|
||||
# set sample rate & bit depth
|
||||
dac.configure_clocks(sample_rate=11030, bit_depth=16)
|
||||
|
||||
if "sound" in launcher_config:
|
||||
if launcher_config["sound"] == "speaker":
|
||||
# use speaker
|
||||
dac.speaker_output = True
|
||||
dac.speaker_volume = -40
|
||||
else:
|
||||
# use headphones
|
||||
dac.headphone_output = True
|
||||
dac.headphone_volume = -15 # dB
|
||||
else:
|
||||
# default to headphones
|
||||
dac.headphone_output = True
|
||||
dac.headphone_volume = -15 # dB
|
||||
|
||||
wave_file = open("/boot_animation/ada_fruitjam_boot_jingle.wav", "rb")
|
||||
wave = WaveFile(wave_file)
|
||||
audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)
|
||||
|
||||
|
||||
class OvershootAnimator:
|
||||
"""
|
||||
A non-blocking animator that moves an element to a target with overshoot effect.
|
||||
|
||||
Instead of blocking with sleep(), this class provides a tick() method that
|
||||
should be called repeatedly by an external loop (e.g., game loop, UI event loop).
|
||||
"""
|
||||
|
||||
def __init__(self, element):
|
||||
"""
|
||||
Initialize the animator with an element to animate.
|
||||
|
||||
Parameters:
|
||||
- element: An object with x and y properties that will be animated
|
||||
"""
|
||||
self.element = element
|
||||
self.pos_animating = False
|
||||
self.start_time = 0
|
||||
self.start_x = 0
|
||||
self.start_y = 0
|
||||
self.target_x = 0
|
||||
self.target_y = 0
|
||||
self.overshoot_x = 0
|
||||
self.overshoot_y = 0
|
||||
self.duration = 0
|
||||
self.overshoot_pixels = 0
|
||||
self.eased_value = None
|
||||
|
||||
self.cur_sprite_index = None
|
||||
self.last_sprite_frame_time = -1
|
||||
self.sprite_anim_start_time = -1
|
||||
self.sprite_anim_from_index = None
|
||||
self.sprite_anim_to_index = None
|
||||
self.sprite_anim_delay = None
|
||||
|
||||
def animate_to(self, target_x, target_y, duration=1.0, overshoot_pixels=20,
|
||||
start_sprite_anim_at=None, sprite_delay=1 / 60,
|
||||
sprite_from_index=None, sprite_to_index=None, eased_value=None):
|
||||
|
||||
"""
|
||||
Start a new animation to the specified target.
|
||||
|
||||
Parameters:
|
||||
- target_x, target_y: The final target coordinates
|
||||
- duration: Total animation time in seconds
|
||||
- overshoot_pixels: How many pixels to overshoot beyond the target
|
||||
(use 0 for no overshoot)
|
||||
"""
|
||||
_now = time.monotonic()
|
||||
|
||||
# Record starting position and time
|
||||
self.start_x = self.element.x
|
||||
self.start_y = self.element.y
|
||||
self.start_time = _now
|
||||
if start_sprite_anim_at is not None:
|
||||
self.sprite_anim_start_time = _now + start_sprite_anim_at
|
||||
self.sprite_anim_to_index = sprite_to_index
|
||||
self.sprite_anim_from_index = sprite_from_index
|
||||
self.cur_sprite_index = self.sprite_anim_from_index
|
||||
self.sprite_anim_delay = sprite_delay
|
||||
|
||||
# Store target position and parameters
|
||||
self.target_x = target_x
|
||||
self.target_y = target_y
|
||||
self.duration = duration
|
||||
self.overshoot_pixels = overshoot_pixels
|
||||
|
||||
# Calculate distance to target
|
||||
dx = target_x - self.start_x
|
||||
dy = target_y - self.start_y
|
||||
|
||||
# Calculate the direction vector (normalized)
|
||||
distance = math.sqrt(dx * dx + dy * dy)
|
||||
if distance <= 0:
|
||||
# Already at target
|
||||
return False
|
||||
|
||||
dir_x = dx / distance
|
||||
dir_y = dy / distance
|
||||
|
||||
# Calculate overshoot position
|
||||
self.overshoot_x = target_x + dir_x * overshoot_pixels
|
||||
self.overshoot_y = target_y + dir_y * overshoot_pixels
|
||||
|
||||
self.eased_value = eased_value
|
||||
|
||||
# Start the animation
|
||||
self.pos_animating = True
|
||||
return True
|
||||
|
||||
def sprite_anim_tick(self, cur_time):
|
||||
if cur_time >= self.last_sprite_frame_time + self.sprite_anim_delay:
|
||||
self.element[0] = self.cur_sprite_index
|
||||
self.last_sprite_frame_time = cur_time
|
||||
self.cur_sprite_index += 1
|
||||
|
||||
if self.cur_sprite_index > self.sprite_anim_to_index:
|
||||
self.cur_sprite_index = None
|
||||
self.sprite_anim_from_index = None
|
||||
self.sprite_anim_to_index = None
|
||||
self.sprite_anim_delay = None
|
||||
self.last_sprite_frame_time = -1
|
||||
self.sprite_anim_start_time = -1
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def tick(self):
|
||||
"""
|
||||
Update the animation based on the current time.
|
||||
|
||||
This method should be called repeatedly until it returns False.
|
||||
|
||||
Returns:
|
||||
- True if the animation is still in progress
|
||||
- False if the animation has completed
|
||||
"""
|
||||
still_sprite_animating = False
|
||||
_now = time.monotonic()
|
||||
if self.cur_sprite_index is not None:
|
||||
if _now >= self.sprite_anim_start_time:
|
||||
still_sprite_animating = self.sprite_anim_tick(_now)
|
||||
# print("sprite_still_animating", still_sprite_animating)
|
||||
if not still_sprite_animating:
|
||||
return False
|
||||
else:
|
||||
if not self.pos_animating:
|
||||
# print("returning false cur_sprite_index was None and pos_animating False")
|
||||
return False
|
||||
|
||||
# Calculate elapsed time and progress
|
||||
elapsed = _now - self.start_time
|
||||
progress = elapsed / self.duration
|
||||
|
||||
# Check if animation is complete
|
||||
if progress >= 1.0:
|
||||
# Ensure we end exactly at the target
|
||||
if self.element.x != self.target_x or self.element.y != self.target_y:
|
||||
self.element.x = self.target_x
|
||||
self.element.y = self.target_y
|
||||
|
||||
self.pos_animating = False
|
||||
if still_sprite_animating:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# Calculate the current position based on progress
|
||||
if self.overshoot_pixels > 0:
|
||||
# Two-phase animation with overshoot
|
||||
if progress < 0.7: # Move smoothly toward overshoot position
|
||||
# Use a single smooth curve to the overshoot point
|
||||
eased = progress / 0.7 # Linear acceleration toward overshoot
|
||||
# Apply slight ease-in to make it accelerate through the target point
|
||||
eased = eased ** 1.2
|
||||
current_x = self.start_x + (self.overshoot_x - self.start_x) * eased
|
||||
current_y = self.start_y + (self.overshoot_y - self.start_y) * eased
|
||||
else: # Return from overshoot to target
|
||||
sub_progress = (progress - 0.7) / 0.3
|
||||
# Decelerate toward final target
|
||||
eased = 1 - (1 - sub_progress) ** 2 # ease-out quad
|
||||
current_x = self.overshoot_x + (self.target_x - self.overshoot_x) * eased
|
||||
current_y = self.overshoot_y + (self.target_y - self.overshoot_y) * eased
|
||||
else:
|
||||
# Simple ease-out when no overshoot is desired
|
||||
if self.eased_value is None:
|
||||
eased = 1 - (1 - progress) ** 4
|
||||
else:
|
||||
eased = progress / self.eased_value
|
||||
current_x = self.start_x + (self.target_x - self.start_x) * eased
|
||||
current_y = self.start_y + (self.target_y - self.start_y) * eased
|
||||
|
||||
# Update element position
|
||||
self.element.x = int(current_x)
|
||||
self.element.y = int(current_y)
|
||||
|
||||
return True
|
||||
|
||||
def is_animating(self):
|
||||
"""Check if an animation is currently in progress."""
|
||||
return self.pos_animating
|
||||
|
||||
def cancel(self):
|
||||
"""Cancel the current animation."""
|
||||
self.pos_animating = False
|
||||
|
||||
|
||||
apple_sprites, apple_sprites_palette = adafruit_imageload.load("/boot_animation/apple_spritesheet.bmp")
|
||||
f_sprites, f_sprites_palette = adafruit_imageload.load("/boot_animation/f_spritesheet.bmp")
|
||||
r_sprites, r_sprites_palette = adafruit_imageload.load("/boot_animation/r_spritesheet.bmp")
|
||||
u_sprites, u_sprites_palette = adafruit_imageload.load("/boot_animation/u_spritesheet.bmp")
|
||||
i_sprites, i_sprites_palette = adafruit_imageload.load("/boot_animation/i_spritesheet.bmp")
|
||||
t_sprites, t_sprites_palette = adafruit_imageload.load("/boot_animation/t_spritesheet.bmp")
|
||||
j_sprites, j_sprites_palette = adafruit_imageload.load("/boot_animation/j_spritesheet.bmp")
|
||||
j_sprites_palette.make_transparent(0)
|
||||
a_sprites, a_sprites_palette = adafruit_imageload.load("/boot_animation/a_spritesheet.bmp")
|
||||
a_sprites_palette.make_transparent(0)
|
||||
m_sprites, m_sprites_palette = adafruit_imageload.load("/boot_animation/m_spritesheet.bmp")
|
||||
m_sprites_palette.make_transparent(0)
|
||||
|
||||
default_sprite_delay = 1 / 35
|
||||
|
||||
main_group = Group()
|
||||
main_group.x = display.width // 2 - BOX_SIZE[0] // 2 - 30
|
||||
main_group.y = display.height // 2 - BOX_SIZE[1] // 2 - 31
|
||||
|
||||
sliding_group = Group()
|
||||
main_group.append(sliding_group)
|
||||
|
||||
letters_x_start = 83
|
||||
letters_y_start = display.height
|
||||
|
||||
apple_tilegrid = TileGrid(apple_sprites, pixel_shader=apple_sprites_palette,
|
||||
tile_width=73, tile_height=107, width=1, height=1)
|
||||
f_tilegrid = TileGrid(f_sprites, pixel_shader=f_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
r_tilegrid = TileGrid(r_sprites, pixel_shader=r_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
u_tilegrid = TileGrid(u_sprites, pixel_shader=u_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
i_tilegrid = TileGrid(i_sprites, pixel_shader=i_sprites_palette,
|
||||
tile_width=16, tile_height=39, width=1, height=1)
|
||||
t_tilegrid = TileGrid(t_sprites, pixel_shader=t_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
j_tilegrid = TileGrid(j_sprites, pixel_shader=j_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
a_tilegrid = TileGrid(a_sprites, pixel_shader=a_sprites_palette,
|
||||
tile_width=32, tile_height=39, width=1, height=1)
|
||||
m_tilegrid = TileGrid(m_sprites, pixel_shader=m_sprites_palette,
|
||||
tile_width=43, tile_height=39, width=1, height=1)
|
||||
|
||||
coordinator = {
|
||||
"steps": [
|
||||
# Apple fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": apple_tilegrid,
|
||||
"offscreen_loc": (0, -207),
|
||||
"onscreen_loc": (0, 21),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 1,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 11),
|
||||
"sprite_delay": 1 / 42,
|
||||
"start_time": 0.0,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
},
|
||||
# F fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": f_tilegrid,
|
||||
"offscreen_loc": (letters_x_start, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start, 67),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 20,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 0.45,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
|
||||
},
|
||||
# R fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": r_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + 32 + 3 - 1, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + 32 + 3 - 1, 67),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 20,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 0.9,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
},
|
||||
# Left slide everything
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": sliding_group,
|
||||
"offscreen_loc": (100, 0),
|
||||
"onscreen_loc": (30, 0),
|
||||
"move_duration": 1.75,
|
||||
"overshoot_pixels": 0,
|
||||
"eased_value": 1,
|
||||
"sprite_anim_range": None,
|
||||
"sprite_delay": None,
|
||||
"start_time": 0.9,
|
||||
"sprite_anim_start": None,
|
||||
"started": False,
|
||||
},
|
||||
# U fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": u_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 2 - 2, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 2 - 2, 67),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 20,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 1.35,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
},
|
||||
# I fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": i_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 3 - 3, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 3 - 3, 67),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 20,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 1.8,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
},
|
||||
# T fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": t_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 3 + 16 + 3 - 4, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 3 + 16 + 3 - 4, 67),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 20,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 2.25,
|
||||
"sprite_anim_start": 0.347,
|
||||
"started": False,
|
||||
},
|
||||
# J fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": j_tilegrid,
|
||||
"offscreen_loc": (letters_x_start, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start, 50 + 39),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 4,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 2.7,
|
||||
# "sprite_anim_start": 0.347,
|
||||
"sprite_anim_start": 0.4,
|
||||
"started": False,
|
||||
},
|
||||
# A fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": a_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + 32 + 3 - 1, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + 32 + 3 - 1, 50 + 39),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 4,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 3.15,
|
||||
"sprite_anim_start": 0.4,
|
||||
"started": False,
|
||||
},
|
||||
# M fly on
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": m_tilegrid,
|
||||
"offscreen_loc": (letters_x_start + 32 + 3 + 32 + 2 - 1, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + 32 + 3 + 32 + 2 - 1, 50 + 39),
|
||||
"move_duration": 0.45,
|
||||
"overshoot_pixels": 4,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (0, 15),
|
||||
"sprite_delay": default_sprite_delay,
|
||||
"start_time": 3.6,
|
||||
"sprite_anim_start": 0.4,
|
||||
"started": False,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
for step in coordinator["steps"]:
|
||||
if isinstance(step["tilegrid"], TileGrid):
|
||||
sliding_group.append(step["tilegrid"])
|
||||
step["default_palette"] = step["tilegrid"].pixel_shader
|
||||
step["tilegrid"].x = step["offscreen_loc"][0]
|
||||
step["tilegrid"].y = step["offscreen_loc"][1]
|
||||
step["animator"] = OvershootAnimator(step["tilegrid"])
|
||||
|
||||
# F bounce up from J impact
|
||||
coordinator["steps"].insert(8,
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][1]["tilegrid"],
|
||||
"animator": coordinator["steps"][1]["animator"],
|
||||
"offscreen_loc": (letters_x_start, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start, 52),
|
||||
"move_duration": 0.3,
|
||||
"overshoot_pixels": 22,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (19, 27),
|
||||
"sprite_delay": 1 / 22,
|
||||
"start_time": 3.0,
|
||||
"sprite_anim_start": 0.15,
|
||||
"started": False,
|
||||
},
|
||||
)
|
||||
# R bounce up from A impact
|
||||
coordinator["steps"].insert(10,
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][2]["tilegrid"],
|
||||
"animator": coordinator["steps"][2]["animator"],
|
||||
"offscreen_loc": (letters_x_start + 32 + 3 - 1, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + 32 + 3 - 1, 52),
|
||||
"move_duration": 0.3,
|
||||
"overshoot_pixels": 22,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (19, 27),
|
||||
"sprite_delay": 1 / 22,
|
||||
"start_time": 3.45,
|
||||
"sprite_anim_start": 0.15,
|
||||
"started": False,
|
||||
},
|
||||
)
|
||||
# U bounce up from M impact
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][4]["tilegrid"],
|
||||
"animator": coordinator["steps"][4]["animator"],
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 2 - 2, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 2 - 2, 52),
|
||||
"move_duration": 0.3,
|
||||
"overshoot_pixels": 22,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (19, 27),
|
||||
"sprite_delay": 1 / 22,
|
||||
"start_time": 3.9,
|
||||
"sprite_anim_start": 0.15,
|
||||
"started": False,
|
||||
},
|
||||
)
|
||||
# I bounce up from M impact
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][5]["tilegrid"],
|
||||
"animator": coordinator["steps"][5]["animator"],
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 3 - 3, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 3 - 3, 52),
|
||||
"move_duration": 0.3,
|
||||
"overshoot_pixels": 22,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (19, 27),
|
||||
"sprite_delay": 1 / 22,
|
||||
"start_time": 4.00,
|
||||
"sprite_anim_start": 0.15,
|
||||
"started": False,
|
||||
},
|
||||
)
|
||||
# T bounce up from M impact
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][6]["tilegrid"],
|
||||
"animator": coordinator["steps"][6]["animator"],
|
||||
"offscreen_loc": (letters_x_start + (32 + 3) * 3 + 16 + 3 - 4, letters_y_start),
|
||||
"onscreen_loc": (letters_x_start + (32 + 3) * 3 + 16 + 3 - 4, 52),
|
||||
"move_duration": 0.3,
|
||||
"overshoot_pixels": 22,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (19, 27),
|
||||
"sprite_delay": 1 / 22,
|
||||
"start_time": 4.1,
|
||||
"sprite_anim_start": 0.15,
|
||||
"started": False,
|
||||
},
|
||||
)
|
||||
# color red
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 4.75,
|
||||
"type": "change_palette",
|
||||
"new_palette": "red_palette",
|
||||
"color": 0xff0000,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# color yellow
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 5,
|
||||
"type": "change_palette",
|
||||
"new_palette": "yellow_palette",
|
||||
"color": 0xffff00,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# color teal
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 5.25,
|
||||
"type": "change_palette",
|
||||
"new_palette": "teal_palette",
|
||||
"color": 0x00ffff,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# color pink
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 5.5,
|
||||
"type": "change_palette",
|
||||
"new_palette": "pink_palette",
|
||||
"color": 0xff00ff,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# color blue
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 5.75,
|
||||
"type": "change_palette",
|
||||
"new_palette": "blue_palette",
|
||||
"color": 0x0000ff,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# color green
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"start_time": 6.00,
|
||||
"type": "change_palette",
|
||||
"new_palette": "green_palette",
|
||||
"color": 0x00ff00,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# Apple eyes blink
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][0]["tilegrid"],
|
||||
"animator": coordinator["steps"][0]["animator"],
|
||||
"offscreen_loc": (0, -207),
|
||||
"onscreen_loc": (0, 21),
|
||||
"move_duration": 0.01,
|
||||
"overshoot_pixels": 0,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (12, 27),
|
||||
"sprite_delay": 1 / 32,
|
||||
"start_time": 6.65,
|
||||
"sprite_anim_start": 0.0,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
# Apple eyes blink again
|
||||
coordinator["steps"].append(
|
||||
{
|
||||
"type": "animation_step",
|
||||
"tilegrid": coordinator["steps"][0]["tilegrid"],
|
||||
"animator": coordinator["steps"][0]["animator"],
|
||||
"offscreen_loc": (0, -207),
|
||||
"onscreen_loc": (0, 21),
|
||||
"move_duration": 0.01,
|
||||
"overshoot_pixels": 0,
|
||||
"eased_value": None,
|
||||
"sprite_anim_range": (12, 18),
|
||||
"sprite_delay": 1 / 32,
|
||||
"start_time": 8.75,
|
||||
"sprite_anim_start": 0.0,
|
||||
"started": False,
|
||||
}
|
||||
)
|
||||
|
||||
display.root_group = main_group
|
||||
|
||||
start_time = time.monotonic()
|
||||
|
||||
if ltv320_present:
|
||||
audio.play(wave)
|
||||
|
||||
while True:
|
||||
now = time.monotonic()
|
||||
still_going = True
|
||||
|
||||
for i in range(len(coordinator["steps"])):
|
||||
step = coordinator["steps"][i]
|
||||
if now - start_time >= step["start_time"]:
|
||||
if not step["started"]:
|
||||
step["started"] = True
|
||||
if step["type"] == "animation_step":
|
||||
if step["sprite_anim_range"] is not None:
|
||||
step["animator"].animate_to(
|
||||
*step["onscreen_loc"],
|
||||
duration=step["move_duration"], overshoot_pixels=step["overshoot_pixels"],
|
||||
start_sprite_anim_at=step["sprite_anim_start"],
|
||||
sprite_from_index=step["sprite_anim_range"][0],
|
||||
sprite_to_index=step["sprite_anim_range"][1],
|
||||
sprite_delay=step["sprite_delay"], eased_value=step["eased_value"],
|
||||
)
|
||||
else:
|
||||
step["animator"].animate_to(
|
||||
*step["onscreen_loc"],
|
||||
duration=step["move_duration"], overshoot_pixels=step["overshoot_pixels"],
|
||||
eased_value=step["eased_value"]
|
||||
)
|
||||
elif step["type"] == "change_palette":
|
||||
# color_sweep_all(step["color"], delay=0)
|
||||
for _cur_step in coordinator["steps"]:
|
||||
if "tilegrid" in _cur_step and isinstance(_cur_step["tilegrid"], TileGrid):
|
||||
_cur_step["tilegrid"].pixel_shader[1] = step["color"]
|
||||
|
||||
if "animator" in step:
|
||||
if i == len(coordinator["steps"]) - 1:
|
||||
still_going = step["animator"].tick()
|
||||
else:
|
||||
step["animator"].tick()
|
||||
else:
|
||||
if i == len(coordinator["steps"]) - 1:
|
||||
still_going = False
|
||||
# display.refresh(target_frames_per_second=TARGET_FPS)
|
||||
display.refresh()
|
||||
|
||||
if not still_going:
|
||||
break
|
||||
|
||||
if ltv320_present:
|
||||
while audio.playing:
|
||||
pass
|
||||
|
||||
supervisor.set_next_code_file("code.py")
|
||||
supervisor.reload()
|
||||
544
src/code.py
|
|
@ -1,17 +1,21 @@
|
|||
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
This example uses adafruit_display_text.label to display text using a custom font
|
||||
loaded by adafruit_bitmap_font
|
||||
Fruit Jam OS Launcher
|
||||
"""
|
||||
import array
|
||||
|
||||
import atexit
|
||||
import json
|
||||
import math
|
||||
import displayio
|
||||
import pathlib
|
||||
import os
|
||||
import supervisor
|
||||
import sys
|
||||
import terminalio
|
||||
import usb
|
||||
|
||||
import adafruit_pathlib as pathlib
|
||||
from adafruit_bitmap_font import bitmap_font
|
||||
from adafruit_display_text.text_box import TextBox
|
||||
from adafruit_display_text.bitmap_label import Label
|
||||
|
|
@ -20,18 +24,71 @@ from adafruit_anchored_tilegrid import AnchoredTileGrid
|
|||
import adafruit_imageload
|
||||
import adafruit_usb_host_descriptors
|
||||
from adafruit_anchored_group import AnchoredGroup
|
||||
from adafruit_fruitjam.peripherals import request_display_config, VALID_DISPLAY_SIZES
|
||||
from adafruit_argv_file import read_argv, write_argv
|
||||
|
||||
"""
|
||||
desktop launcher code.py arguments
|
||||
|
||||
0: next code files
|
||||
1-N: args to pass to next code file
|
||||
|
||||
"""
|
||||
|
||||
args = read_argv(__file__)
|
||||
if args is not None and len(args) > 0:
|
||||
next_code_file = None
|
||||
remaining_args = None
|
||||
if len(args) > 0:
|
||||
next_code_file = args[0]
|
||||
if len(args) > 1:
|
||||
remaining_args = args[1:]
|
||||
|
||||
if remaining_args is not None:
|
||||
write_argv(next_code_file, remaining_args)
|
||||
|
||||
next_code_file = next_code_file
|
||||
supervisor.set_next_code_file(next_code_file, sticky_on_reload=False, reload_on_error=True,
|
||||
working_directory="/".join(next_code_file.split("/")[:-1]))
|
||||
print(f"launching: {next_code_file}")
|
||||
supervisor.reload()
|
||||
|
||||
if (width_config := os.getenv("CIRCUITPY_DISPLAY_WIDTH")) is not None:
|
||||
if width_config not in [x[0] for x in VALID_DISPLAY_SIZES]:
|
||||
raise ValueError(f"Invalid display size. Must be one of: {VALID_DISPLAY_SIZES}")
|
||||
for display_size in VALID_DISPLAY_SIZES:
|
||||
if display_size[0] == width_config:
|
||||
break
|
||||
else:
|
||||
display_size = (720, 400)
|
||||
request_display_config(*display_size)
|
||||
display = supervisor.runtime.display
|
||||
|
||||
scale = 1
|
||||
if display.width > 360:
|
||||
scale = 2
|
||||
|
||||
launcher_config = {}
|
||||
if pathlib.Path("launcher.conf.json").exists():
|
||||
with open("launcher.conf.json", "r") as f:
|
||||
launcher_config = json.load(f)
|
||||
if "palette" not in launcher_config:
|
||||
launcher_config["palette"] = {}
|
||||
|
||||
font_file = "/fonts/terminal.lvfontbin"
|
||||
font = bitmap_font.load_font(font_file)
|
||||
scaled_group = displayio.Group(scale=scale)
|
||||
|
||||
main_group = displayio.Group()
|
||||
main_group.append(scaled_group)
|
||||
|
||||
display.root_group = main_group
|
||||
|
||||
background_bmp = displayio.Bitmap(display.width, display.height, 1)
|
||||
bg_palette = displayio.Palette(1)
|
||||
bg_palette[0] = 0x222222
|
||||
bg_palette[0] = int(launcher_config["palette"].get("bg", "0x222222"), 16)
|
||||
bg_tg = displayio.TileGrid(bitmap=background_bmp, pixel_shader=bg_palette)
|
||||
main_group.append(bg_tg)
|
||||
scaled_group.append(bg_tg)
|
||||
|
||||
# load the mouse cursor bitmap
|
||||
mouse_bmp = displayio.OnDiskBitmap("launcher_assets/mouse_cursor.bmp")
|
||||
|
|
@ -43,19 +100,22 @@ mouse_bmp.pixel_shader.make_transparent(0)
|
|||
mouse_tg = displayio.TileGrid(mouse_bmp, pixel_shader=mouse_bmp.pixel_shader)
|
||||
|
||||
# move it to the center of the display
|
||||
mouse_tg.x = display.width // 2
|
||||
mouse_tg.y = display.height // 2
|
||||
mouse_tg.x = display.width // (2 * scale)
|
||||
mouse_tg.y = display.height // (2 * scale)
|
||||
# 046d:c52f
|
||||
|
||||
|
||||
# mouse = usb.core.find(idVendor=0x046d, idProduct=0xc52f)
|
||||
|
||||
DIR_IN = 0x80
|
||||
mouse_interface_index, mouse_endpoint_address = None, None
|
||||
mouse = None
|
||||
# scan for connected USB device and loop over any found
|
||||
print("scanning usb")
|
||||
for device in usb.core.find(find_all=True):
|
||||
mouse_was_attached = None
|
||||
|
||||
if "use_mouse" in launcher_config and launcher_config["use_mouse"]:
|
||||
|
||||
# scan for connected USB device and loop over any found
|
||||
print("scanning usb")
|
||||
for device in usb.core.find(find_all=True):
|
||||
# print device info
|
||||
print(f"{device.idVendor:04x}:{device.idProduct:04x}")
|
||||
print(device.manufacturer, device.product)
|
||||
|
|
@ -64,184 +124,402 @@ for device in usb.core.find(find_all=True):
|
|||
device, 0
|
||||
)
|
||||
print(config_descriptor)
|
||||
#
|
||||
# i = 0
|
||||
# while i < len(config_descriptor):
|
||||
# descriptor_len = config_descriptor[i]
|
||||
# descriptor_type = config_descriptor[i + 1]
|
||||
# if descriptor_type == adafruit_usb_host_descriptors.DESC_CONFIGURATION:
|
||||
# config_value = config_descriptor[i + 5]
|
||||
# print(f" value {config_value:d}")
|
||||
# elif descriptor_type == adafruit_usb_host_descriptors.DESC_INTERFACE:
|
||||
# interface_number = config_descriptor[i + 2]
|
||||
# interface_class = config_descriptor[i + 5]
|
||||
# interface_subclass = config_descriptor[i + 6]
|
||||
# interface_protocol = config_descriptor[i + 7]
|
||||
# print(f" interface[{interface_number:d}]")
|
||||
# print(
|
||||
# f" class {interface_class:02x} subclass {interface_subclass:02x}"
|
||||
# )
|
||||
# print(f"protocol: {interface_protocol}")
|
||||
# elif descriptor_type == adafruit_usb_host_descriptors.DESC_ENDPOINT:
|
||||
# endpoint_address = config_descriptor[i + 2]
|
||||
# if endpoint_address & DIR_IN:
|
||||
# print(f" IN {endpoint_address:02x}")
|
||||
# else:
|
||||
# print(f" OUT {endpoint_address:02x}")
|
||||
# i += descriptor_len
|
||||
# print()
|
||||
#
|
||||
# # assume the device is the mouse
|
||||
# mouse = device
|
||||
mouse_interface_index, mouse_endpoint_address = adafruit_usb_host_descriptors.find_boot_mouse_endpoint(device)
|
||||
if mouse_interface_index is not None and mouse_endpoint_address is not None:
|
||||
|
||||
_possible_interface_index, _possible_endpoint_address = adafruit_usb_host_descriptors.find_boot_mouse_endpoint(device)
|
||||
if _possible_interface_index is not None and _possible_endpoint_address is not None:
|
||||
mouse = device
|
||||
mouse_interface_index = _possible_interface_index
|
||||
mouse_endpoint_address = _possible_endpoint_address
|
||||
print(f"mouse interface: {mouse_interface_index} endpoint_address: {hex(mouse_endpoint_address)}")
|
||||
|
||||
if mouse is not None:
|
||||
mouse_was_attached = None
|
||||
if mouse is not None:
|
||||
# detach the kernel driver if needed
|
||||
if mouse.is_kernel_driver_active(0):
|
||||
mouse_was_attached = True
|
||||
mouse.detach_kernel_driver(0)
|
||||
else:
|
||||
mouse_was_attached = False
|
||||
|
||||
# set configuration on the mouse so we can use it
|
||||
mouse.set_configuration()
|
||||
|
||||
mouse_buf = array.array("b", [0] * 8)
|
||||
WIDTH = 300
|
||||
HEIGHT = 192
|
||||
mouse_buf = array.array("b", [0] * 8)
|
||||
|
||||
WIDTH = int(298 / 360 * display.width // scale)
|
||||
HEIGHT = int(182 / 200 * display.height // scale)
|
||||
|
||||
config = {
|
||||
"menu_title": "Launcher Menu",
|
||||
"width": 3,
|
||||
"height": 2,
|
||||
"apps": [
|
||||
{
|
||||
"title": "🐍Snake🐍",
|
||||
"icon": "icon_snake.bmp",
|
||||
"file": "code_snake_game.py"
|
||||
},
|
||||
{
|
||||
"title": "Nyan😺Flap",
|
||||
"icon": "icon_flappynyan.bmp",
|
||||
"file": "code_flappy_nyan.py"
|
||||
},
|
||||
{
|
||||
"title": "Memory🧠",
|
||||
"icon": "icon_memory.bmp",
|
||||
"file": "code_memory.py"
|
||||
},
|
||||
{
|
||||
"title": "Matrix",
|
||||
"icon": "/apps/matrix/icon.bmp",
|
||||
"file": "/apps/matrix/code.py"
|
||||
},
|
||||
{
|
||||
"title": "Breakout",
|
||||
"icon": "icon_breakout.bmp",
|
||||
"file": "code_breakout.py"
|
||||
},
|
||||
{
|
||||
"title": "Paint🖌️",
|
||||
"icon": "icon_paint.bmp",
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
cell_width = WIDTH // config["width"]
|
||||
cell_height = HEIGHT // config["height"]
|
||||
page_size = config["width"] * config["height"]
|
||||
|
||||
default_icon_bmp, default_icon_palette = adafruit_imageload.load("launcher_assets/default_icon.bmp")
|
||||
default_icon_palette.make_transparent(0)
|
||||
menu_grid = GridLayout(x=10, y=26, width=WIDTH, height=HEIGHT, grid_size=(config["width"], config["height"]),
|
||||
menu_grid = GridLayout(x=(display.width // scale - WIDTH) // 2,
|
||||
y=(display.height // scale - HEIGHT) // 2,
|
||||
width=WIDTH, height=HEIGHT, grid_size=(config["width"], config["height"]),
|
||||
divider_lines=False)
|
||||
main_group.append(menu_grid)
|
||||
scaled_group.append(menu_grid)
|
||||
|
||||
menu_title_txt = Label(font, text=config["menu_title"])
|
||||
menu_title_txt = Label(font, text="Fruit Jam OS", color=int(launcher_config["palette"].get("fg", "0xffffff"), 16))
|
||||
menu_title_txt.anchor_point = (0.5, 0.5)
|
||||
menu_title_txt.anchored_position = (display.width // 2, 2)
|
||||
main_group.append(menu_title_txt)
|
||||
menu_title_txt.anchored_position = (display.width // (2 * scale), 2)
|
||||
scaled_group.append(menu_title_txt)
|
||||
|
||||
app_titles = []
|
||||
apps = []
|
||||
app_path = pathlib.Path("/apps")
|
||||
i = 0
|
||||
|
||||
pages = [{}]
|
||||
|
||||
cur_file_index = 0
|
||||
|
||||
for path in app_path.iterdir():
|
||||
print(path)
|
||||
|
||||
code_file = path / "code.py"
|
||||
if not code_file.exists():
|
||||
continue
|
||||
cell_group = AnchoredGroup()
|
||||
|
||||
metadata_file = path / "metadata.json"
|
||||
if not metadata_file.exists():
|
||||
metadata_file = None
|
||||
metadata = None
|
||||
if metadata_file is not None:
|
||||
with open(metadata_file.absolute(), "r") as f:
|
||||
metadata = json.load(f)
|
||||
|
||||
if metadata is not None and "icon" in metadata:
|
||||
icon_file = path / metadata["icon"]
|
||||
else:
|
||||
icon_file = path / "icon.bmp"
|
||||
|
||||
if not icon_file.exists():
|
||||
icon_file = None
|
||||
|
||||
if metadata is not None and "title" in metadata:
|
||||
title = metadata["title"]
|
||||
else:
|
||||
title = path.name
|
||||
|
||||
apps.append({
|
||||
"title": path.name,
|
||||
"icon": str(icon_file.absolute()),
|
||||
"file": str(code_file.absolute())
|
||||
"title": title,
|
||||
"icon": str(icon_file.absolute()) if icon_file is not None else None,
|
||||
"file": str(code_file.absolute()),
|
||||
"dir": path
|
||||
})
|
||||
if apps[-1]["icon"] is None:
|
||||
|
||||
apps = sorted(apps, key=lambda app: app["title"].lower())
|
||||
|
||||
print("launcher config", launcher_config)
|
||||
if "favorites" in launcher_config:
|
||||
|
||||
for favorite_app in reversed(launcher_config["favorites"]):
|
||||
print("checking favorite", favorite_app)
|
||||
for app in apps:
|
||||
print(f"checking app: {app["dir"]}")
|
||||
if app["dir"] == f"/apps/{favorite_app}":
|
||||
apps.remove(app)
|
||||
apps.insert(0, app)
|
||||
|
||||
|
||||
def reuse_cell(grid_coords):
|
||||
try:
|
||||
cell_group = menu_grid.get_content(grid_coords)
|
||||
return cell_group
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
def _create_cell_group(app):
|
||||
cell_group = AnchoredGroup()
|
||||
|
||||
if app["icon"] is None:
|
||||
icon_tg = displayio.TileGrid(bitmap=default_icon_bmp, pixel_shader=default_icon_palette)
|
||||
cell_group.append(icon_tg)
|
||||
else:
|
||||
icon_bmp, icon_palette = adafruit_imageload.load(apps[-1]["icon"])
|
||||
icon_bmp, icon_palette = adafruit_imageload.load(app["icon"])
|
||||
icon_tg = displayio.TileGrid(bitmap=icon_bmp, pixel_shader=icon_palette)
|
||||
cell_group.append(icon_tg)
|
||||
|
||||
icon_tg.x = cell_width // 2 - icon_tg.tile_width // 2
|
||||
title_txt = TextBox(font, text=apps[-1]["title"], width=WIDTH // config["width"], height=18,
|
||||
align=TextBox.ALIGN_CENTER)
|
||||
title_txt = TextBox(font, text=app["title"], width=cell_width, height=18,
|
||||
align=TextBox.ALIGN_CENTER, color=int(launcher_config["palette"].get("fg", "0xffffff"), 16))
|
||||
icon_tg.y = (cell_height - icon_tg.tile_height - title_txt.height) // 2
|
||||
cell_group.append(title_txt)
|
||||
title_txt.anchor_point = (0, 0)
|
||||
title_txt.anchored_position = (0, icon_tg.y + icon_tg.tile_height)
|
||||
app_titles.append(title_txt)
|
||||
menu_grid.add_content(cell_group, grid_position=(i % config["width"], i // config["width"]), cell_size=(1, 1))
|
||||
i += 1
|
||||
return cell_group
|
||||
|
||||
|
||||
def _reuse_cell_group(app, cell_group):
|
||||
_unhide_cell_group(cell_group)
|
||||
if app["icon"] is None:
|
||||
icon_tg = cell_group[0]
|
||||
icon_tg.bitmap = default_icon_bmp
|
||||
icon_tg.pixel_shader = default_icon_palette
|
||||
else:
|
||||
icon_bmp, icon_palette = adafruit_imageload.load(app["icon"])
|
||||
icon_tg = cell_group[0]
|
||||
icon_tg.bitmap = icon_bmp
|
||||
icon_tg.pixel_shader = icon_palette
|
||||
|
||||
icon_tg.x = cell_width // 2 - icon_tg.tile_width // 2
|
||||
# title_txt = TextBox(font, text=app["title"], width=cell_width, height=18,
|
||||
# align=TextBox.ALIGN_CENTER, color=int(launcher_config["palette"].get("fg", "0xffffff"), 16))
|
||||
# cell_group.append(title_txt)
|
||||
title_txt = cell_group[1]
|
||||
title_txt.text = app["title"]
|
||||
# title_txt.anchor_point = (0, 0)
|
||||
# title_txt.anchored_position = (0, icon_tg.y + icon_tg.tile_height)
|
||||
|
||||
|
||||
def _hide_cell_group(cell_group):
|
||||
# hide the tilegrid
|
||||
cell_group[0].hidden = True
|
||||
# set the title to blank space
|
||||
cell_group[1].text = " "
|
||||
|
||||
|
||||
def _unhide_cell_group(cell_group):
|
||||
# show tilegrid
|
||||
cell_group[0].hidden = False
|
||||
|
||||
|
||||
def display_page(page_index):
|
||||
max_pages = math.ceil(len(apps) / page_size)
|
||||
page_txt.text = f"{page_index + 1}/{max_pages}"
|
||||
|
||||
for grid_index in range(page_size):
|
||||
grid_pos = (grid_index % config["width"], grid_index // config["width"])
|
||||
try:
|
||||
cur_app = apps[grid_index + (page_index * page_size)]
|
||||
except IndexError:
|
||||
try:
|
||||
cell_group = menu_grid.get_content(grid_pos)
|
||||
_hide_cell_group(cell_group)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# skip to the next for loop iteration
|
||||
continue
|
||||
|
||||
try:
|
||||
cell_group = menu_grid.get_content(grid_pos)
|
||||
_reuse_cell_group(cur_app, cell_group)
|
||||
except KeyError:
|
||||
cell_group = _create_cell_group(cur_app)
|
||||
menu_grid.add_content(cell_group, grid_position=grid_pos, cell_size=(1, 1))
|
||||
|
||||
# app_titles.append(title_txt)
|
||||
print(f"{grid_index} | {grid_index % config["width"], grid_index // config["width"]}")
|
||||
|
||||
|
||||
page_txt = Label(terminalio.FONT, text="", scale=scale, color=int(launcher_config["palette"].get("fg", "0xffffff"), 16))
|
||||
page_txt.anchor_point = (1.0, 1.0)
|
||||
page_txt.anchored_position = (display.width - 2, display.height - 2)
|
||||
main_group.append(page_txt)
|
||||
|
||||
cur_page = 0
|
||||
display_page(cur_page)
|
||||
|
||||
left_bmp, left_palette = adafruit_imageload.load("launcher_assets/arrow_left.bmp")
|
||||
left_palette.make_transparent(0)
|
||||
right_bmp, right_palette = adafruit_imageload.load("launcher_assets/arrow_right.bmp")
|
||||
right_palette.make_transparent(0)
|
||||
if "arrow" in launcher_config["palette"]:
|
||||
left_palette[2] = right_palette[2] = int(launcher_config["palette"].get("arrow"), 16)
|
||||
|
||||
left_tg = AnchoredTileGrid(bitmap=left_bmp, pixel_shader=left_palette)
|
||||
left_tg.anchor_point = (0, 1.0)
|
||||
left_tg.anchored_position = (10, display.height - 2)
|
||||
left_tg.anchor_point = (0, 0.5)
|
||||
left_tg.anchored_position = (0, (display.height // 2 // scale) - 2)
|
||||
|
||||
right_tg = AnchoredTileGrid(bitmap=right_bmp, pixel_shader=right_palette)
|
||||
right_tg.anchor_point = (1.0, 1.0)
|
||||
right_tg.anchored_position = (display.width - 10, display.height - 2)
|
||||
right_tg.anchor_point = (1.0, 0.5)
|
||||
right_tg.anchored_position = ((display.width // scale), (display.height // 2 // scale) - 2)
|
||||
original_arrow_btn_color = left_palette[2]
|
||||
|
||||
main_group.append(left_tg)
|
||||
main_group.append(right_tg)
|
||||
scaled_group.append(left_tg)
|
||||
scaled_group.append(right_tg)
|
||||
|
||||
if len(apps) <= page_size:
|
||||
right_tg.hidden = True
|
||||
left_tg.hidden = True
|
||||
|
||||
if mouse:
|
||||
main_group.append(mouse_tg)
|
||||
scaled_group.append(mouse_tg)
|
||||
|
||||
selected = 0
|
||||
|
||||
help_txt = Label(terminalio.FONT, text="[Arrow]: Move [E]: Edit [Enter]: Run [1-9]: Page",
|
||||
color=int(launcher_config["palette"].get("fg", "0xffffff"), 16))
|
||||
|
||||
help_txt.anchor_point = (0.0, 1.0)
|
||||
help_txt.anchored_position = (2, display.height-2)
|
||||
|
||||
print(help_txt.bounding_box)
|
||||
main_group.append(help_txt)
|
||||
|
||||
|
||||
def atexit_callback():
|
||||
"""
|
||||
re-attach USB devices to kernel if needed.
|
||||
:return:
|
||||
"""
|
||||
print("inside atexit callback")
|
||||
if mouse_was_attached and not mouse.is_kernel_driver_active(0):
|
||||
mouse.attach_kernel_driver(0)
|
||||
|
||||
|
||||
atexit.register(atexit_callback)
|
||||
|
||||
selected = None
|
||||
|
||||
|
||||
def change_selected(new_selected):
|
||||
global selected
|
||||
# tuple means an item in the grid is selected
|
||||
if isinstance(selected, tuple):
|
||||
menu_grid.get_content(selected)[1].background_color = None
|
||||
|
||||
# TileGrid means arrow is selected
|
||||
elif isinstance(selected, AnchoredTileGrid):
|
||||
selected.pixel_shader[2] = original_arrow_btn_color
|
||||
|
||||
# tuple means an item in the grid is selected
|
||||
if isinstance(new_selected, tuple):
|
||||
menu_grid.get_content(new_selected)[1].background_color = int(launcher_config["palette"].get("accent", "0x008800"), 16)
|
||||
# TileGrid means arrow is selected
|
||||
elif isinstance(new_selected, AnchoredTileGrid):
|
||||
new_selected.pixel_shader[2] = int(launcher_config["palette"].get("accent", "0x008800"), 16)
|
||||
selected = new_selected
|
||||
|
||||
|
||||
change_selected((0, 0))
|
||||
|
||||
def page_right():
|
||||
global cur_page
|
||||
if cur_page < math.ceil(len(apps) / page_size) - 1:
|
||||
cur_page += 1
|
||||
display_page(cur_page)
|
||||
|
||||
def page_left():
|
||||
global cur_page
|
||||
if cur_page > 0:
|
||||
cur_page -= 1
|
||||
display_page(cur_page)
|
||||
|
||||
|
||||
def handle_key_press(key):
|
||||
global index, editor_index, cur_page
|
||||
# print(key)
|
||||
# up key
|
||||
if key == "\x1b[A":
|
||||
if isinstance(selected, tuple):
|
||||
change_selected((selected[0], (selected[1] - 1) % config["height"]))
|
||||
elif selected is left_tg:
|
||||
change_selected((0, 0))
|
||||
elif selected is right_tg:
|
||||
change_selected((2, 0))
|
||||
|
||||
|
||||
# down key
|
||||
elif key == "\x1b[B":
|
||||
if isinstance(selected, tuple):
|
||||
change_selected((selected[0], (selected[1] + 1) % config["height"]))
|
||||
elif selected is left_tg:
|
||||
change_selected((0, 1))
|
||||
elif selected is right_tg:
|
||||
change_selected((2, 1))
|
||||
# selected = min(len(config["apps"]) - 1, selected + 1)
|
||||
|
||||
# left key
|
||||
elif key == "\x1b[D":
|
||||
if isinstance(selected, tuple):
|
||||
if selected[0] >= 1:
|
||||
change_selected((selected[0] - 1, selected[1]))
|
||||
elif not left_tg.hidden:
|
||||
change_selected(left_tg)
|
||||
else:
|
||||
change_selected(((selected[0] - 1) % config["width"], selected[1]))
|
||||
elif selected is left_tg:
|
||||
change_selected(right_tg)
|
||||
elif selected is right_tg:
|
||||
change_selected((2, 0))
|
||||
|
||||
# right key
|
||||
elif key == "\x1b[C":
|
||||
if isinstance(selected, tuple):
|
||||
if selected[0] <= 1:
|
||||
change_selected((selected[0] + 1, selected[1]))
|
||||
elif not right_tg.hidden:
|
||||
change_selected(right_tg)
|
||||
else:
|
||||
change_selected(((selected[0] + 1) % config["width"], selected[1]))
|
||||
elif selected is left_tg:
|
||||
change_selected((0, 0))
|
||||
elif selected is right_tg:
|
||||
change_selected(left_tg)
|
||||
|
||||
elif key == "\n":
|
||||
if isinstance(selected, tuple):
|
||||
index = (selected[1] * config["width"] + selected[0]) + (cur_page * page_size)
|
||||
if index >= len(apps):
|
||||
index = None
|
||||
print("go!")
|
||||
elif selected is left_tg:
|
||||
page_left()
|
||||
elif selected is right_tg:
|
||||
page_right()
|
||||
elif key == "e":
|
||||
if isinstance(selected, tuple):
|
||||
editor_index = (selected[1] * config["width"] + selected[0]) + (cur_page * page_size)
|
||||
if editor_index >= len(apps):
|
||||
editor_index = None
|
||||
|
||||
print("go!")
|
||||
elif key in "123456789":
|
||||
if key != "9":
|
||||
requested_page = int(key)
|
||||
max_page = math.ceil(len(apps) / page_size)
|
||||
if requested_page <= max_page:
|
||||
cur_page = requested_page - 1
|
||||
display_page(requested_page-1)
|
||||
else: # key == 9
|
||||
max_page = math.ceil(len(apps) / page_size)
|
||||
cur_page = max_page - 1
|
||||
display_page(max_page - 1)
|
||||
else:
|
||||
print(f"unhandled key: {repr(key)}")
|
||||
|
||||
|
||||
print(f"apps: {apps}")
|
||||
print(mouse_interface_index, mouse_endpoint_address)
|
||||
while True:
|
||||
index = None
|
||||
editor_index = None
|
||||
|
||||
available = supervisor.runtime.serial_bytes_available
|
||||
if available:
|
||||
c = sys.stdin.read(available)
|
||||
print(repr(c))
|
||||
app_titles[selected].background_color = None
|
||||
# app_titles[selected].background_color = None
|
||||
|
||||
if c == "\x1b[A":
|
||||
selected = max(0, selected - 1)
|
||||
elif c == "\x1b[B":
|
||||
selected = min(len(config["apps"]) - 1, selected + 1)
|
||||
elif c == "\n":
|
||||
index = selected
|
||||
print("go!")
|
||||
handle_key_press(c)
|
||||
print("selected", selected)
|
||||
app_titles[selected].background_color = 0x008800
|
||||
# app_titles[selected].background_color = int(launcher_config["palette"].get("accent", "0x008800"), 16)
|
||||
|
||||
if mouse:
|
||||
try:
|
||||
# attempt to read data from the mouse
|
||||
# 10ms timeout, so we don't block long if there
|
||||
# is no data
|
||||
count = mouse.read(mouse_endpoint_address, mouse_buf, timeout=10)
|
||||
count = mouse.read(mouse_endpoint_address, mouse_buf, timeout=20)
|
||||
except usb.core.USBTimeoutError:
|
||||
# skip the rest of the loop if there is no data
|
||||
count = 0
|
||||
|
|
@ -249,18 +527,38 @@ while True:
|
|||
# update the mouse tilegrid x and y coordinates
|
||||
# based on the delta values read from the mouse
|
||||
if count > 0:
|
||||
mouse_tg.x = max(0, min(display.width - 1, mouse_tg.x + mouse_buf[1]))
|
||||
mouse_tg.y = max(0, min(display.height - 1, mouse_tg.y + mouse_buf[2]))
|
||||
mouse_tg.x = max(0, min((display.width // scale) - 1, mouse_tg.x + mouse_buf[1]))
|
||||
mouse_tg.y = max(0, min((display.height // scale) - 1, mouse_tg.y + mouse_buf[2]))
|
||||
|
||||
if mouse_buf[0] & (1 << 0) != 0:
|
||||
print("left click")
|
||||
clicked_cell = menu_grid.which_cell_contains((mouse_tg.x, mouse_tg.y))
|
||||
if clicked_cell is not None:
|
||||
index = clicked_cell[1] * config["width"] + clicked_cell[0]
|
||||
index = (clicked_cell[1] * config["width"] + clicked_cell[0]) + (cur_page * page_size)
|
||||
|
||||
if right_tg.contains((mouse_tg.x, mouse_tg.y, 0)):
|
||||
page_right()
|
||||
if left_tg.contains((mouse_tg.x, mouse_tg.y, 0)):
|
||||
page_left()
|
||||
|
||||
|
||||
if index is not None:
|
||||
supervisor.set_next_code_file(config["apps"][index]["file"], sticky_on_reload=True, reload_on_error=True,
|
||||
working_directory="/apps/matrix")
|
||||
|
||||
if mouse and not mouse.is_kernel_driver_active(0):
|
||||
mouse.attach_kernel_driver(0)
|
||||
print("index", index)
|
||||
print(f"selected: {apps[index]}")
|
||||
launch_file = apps[index]["file"]
|
||||
supervisor.set_next_code_file(launch_file, sticky_on_reload=False, reload_on_error=True,
|
||||
working_directory="/".join(launch_file.split("/")[:-1]))
|
||||
supervisor.reload()
|
||||
if editor_index is not None:
|
||||
print("editor_index", editor_index)
|
||||
print(f"editor selected: {apps[editor_index]}")
|
||||
edit_file = apps[editor_index]["file"]
|
||||
|
||||
editor_launch_file = "apps/editor/code.py"
|
||||
write_argv(editor_launch_file, [apps[editor_index]["file"]])
|
||||
# with open(argv_filename(launch_file), "w") as f:
|
||||
# f.write(json.dumps([apps[editor_index]["file"]]))
|
||||
|
||||
supervisor.set_next_code_file(editor_launch_file, sticky_on_reload=False, reload_on_error=True,
|
||||
working_directory="/".join(editor_launch_file.split("/")[:-1]))
|
||||
supervisor.reload()
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 918 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 918 B |
1
src/settings.toml
Executable file
|
|
@ -0,0 +1 @@
|
|||
CIRCUITPY_PYSTACK_SIZE=4000
|
||||