This commit is contained in:
osiris account 2023-03-12 15:32:23 -07:00
parent 0696b08d04
commit 314bdf533e
94 changed files with 11 additions and 7 deletions

View file

@ -0,0 +1,28 @@
.PHONY: setup install clean test lint
default: test
setup:
pip install -r requirements.txt
install:
python setup.py install
clean:
@find . -type f -name '*.pyc' -delete
@find . -type d -name '__pycache__' | xargs rm -rf
@find . -type d -name '*.ropeproject' | xargs rm -rf
@rm -rf build/
@rm -rf dist/
@rm -rf venv/
@rm -f src/*.egg*
@rm -f MANIFEST
@rm -rf docs/build/
@rm -f .coverage.*
test:
@tox -- -s
lint:
@tox -e lint

View file

@ -0,0 +1,85 @@
# Enconding decimals
This program:
i) converts and encodes a 14-bit decimal input value to a 2-byte hexadecimal,
ii) decodes hexadecimal representations to 14-bit decimal
## Installing
Create and source virtual enviroment. You can use [virtualenv](https://virtualenv.pypa.io/en/latest/) or [conda](https://docs.conda.io/en/latest/):
```bash
virtualenv venv
source venv/bin/activate
```
Install dependencies:
```bash
make setup
```
Install Efun:
```bash
make install
```
## Usage
### Encoding
To enconde a integer, run:
```bash
efun -e <integer>
```
Note that the value must be in the range `[-8192, 8191]`.
#### Decoding
To decode an integer, run:
```bash
efun -d <integer>
```
Note that the value must be in the range `[0x0000, 0x7F7F]`.
## Developer
### Running tests
You can run tests with:
```bash
make test
```
### Linting
You can lint your code with:
```bash
make lint
```
### Cleaning up
Clean residual compilation and installation files with:
```bash
make clean
```
----
Thank you for reading my code!

View file

@ -0,0 +1,13 @@
# Encoding function
6111 -> 6f5f
340 -> 4254
-2628 -> 2b3c
-255 -> 3e01
7550 -> 7a7e
# Decoding function
0A0A -> -6902
0029 -> -8151
3F0F -> -113
4400 -> 512
5E7F -> 3967

View file

@ -0,0 +1,41 @@
#!/usr/bin/env python
import unittest
import src.main as m
class EfunTest(unittest.TestCase):
def setUp(self):
self.e = m.Efun()
self.test_data = [
(-4096, 0x2000, (0x20, 0x00)),
(-8192, 0x0000, (0x00, 0x00)),
(0, 0x4000, (0x40, 0x00)),
(2048, 0x5000, (0x50, 0x00)),
(8191, 0x7F7F, (0x7F, 0x7F)),
(6111, 0x6F5F, (0x6F, 0x5F)),
(340, 0x4254, (0x42, 0x54)),
(-2628, 0x2B3C, (0x2B, 0x3C)),
(-255, 0x3E01, (0x3E, 0x01)),
(-6902, 0x0A0A, (0x0A, 0x0A)),
(-8151, 0x0029, (0x00, 0x29)),
(-113, 0x3F0F, (0x3F, 0x0F)),
(512, 0x4400, (0x44, 0x00)),
(3967, 0x5E7F, (0x5E, 0x7F)),
]
def test_extract(self):
for item in self.test_data:
self.assertEqual(self.e._extract_two_bytes(item[1]), item[2])
def test_decode(self):
for item in self.test_data:
by1, by2 = self.e._extract_two_bytes(item[1])
self.e._decode(by1, by2)
self.assertEqual(self.e.dec_byte, item[0])
def test_encode(self):
for item in self.test_data:
self.e._encode(item[0])
self.assertEqual(self.e.enc_byte, hex(item[1]))

View file

@ -0,0 +1,2 @@
tox==3.14.3
virtualenv==20.0.1

View file

@ -0,0 +1,15 @@
from setuptools import setup, find_packages
setup(
name='efun',
version='0.0.1',
packages=find_packages(),
include_package_data=True,
author='steinkirch',
install_requires=[
],
entry_points='''
[console_scripts]
efun=src.main:main
''',
)

View file

@ -0,0 +1 @@
# -*- coding: utf8 -*-

View file

@ -0,0 +1,154 @@
#!/usr/bin/env python
import argparse
class Efun(object):
def __init__(self):
self.enc_byte = None
self.dec_byte = None
self.enc_range = [-8192, 8191]
self.dec_range = [0x0000, 0x7F7F]
def _decode(self, higher_byte, lower_byte):
"""
Converts encoded hex value back into the original signed integer.
Argument:
higher_byte {string} -- higher byter hex input to be decoded.
lower_byte {string} -- lower byter hex input to be decoded.
Returns:
Decoded value string in decimal base.
"""
# Left shift 7 to get higher byte.
higher_byte <<= 7
# 'OR' byte withing the decimal lower limit.
self.dec_byte = (lower_byte | higher_byte) + self.enc_range[0]
def _encode(self, value):
"""
Gets a signed integer and return a 4 character string,
such that after the encoding is complete, the most
significant bit of each byte has been cleared.
Argument:
value {string} -- integer input value to be encoded.
Returns:
Encoded 2-byte string.
"""
# Add 8192 for unsigned
sign_int = int(value)
unsign_int = sign_int + 8192
# 'AND' with 0b0000000001111111 for lower byte and
# 'AND' with 0b0011111110000000 for higher byter
lower_byte = unsign_int & 0x007F
higher_byte = ((unsign_int & 0x3F80) << 1)
# Return hex enconding of sum of the two bytes
self.enc_byte = hex(lower_byte + higher_byte)
def _extract_two_bytes(self, value):
"""
Extract two bytes from a hexadecimal string.
Arguments:
value {hex} -- hexadecimal between 0x0000 and 0x7F7F.
Returns:
higher_byte, lower_byte -- byte strings.
"""
# 'And' with 0b0000000001111111 for lower byte
lower_byte = value & 0x00FF
# Left shift 8 positions for higher byte
higher_byte = value >> 8
return higher_byte, lower_byte
def run_decode(self, value):
"""
Verifies if an input is valid 2-byte hexadecimal,
returning its decoded value.
Arguments:
value {string} -- hexadecimal input value.
Returns:
None.
"""
# Convert to hexadecimal.
try:
value = int(value, 16)
except ValueError as e:
print('Could not convert value to hexadecimal: {}.'.fomart(e))
return -1
# Verify whether the value is within the range,
# and then decode it.
if self.dec_range[0] <= value <= self.dec_range[1]:
self._decode(*self._extract_two_bytes(value))
print('{0} decodes as {1}'.format(hex(value), self.dec_byte))
else:
print('Value {0} is out of range of [{1}, {2}]'.format(hex(value),
hex(self.dec_range[0], hex(self.dec_range[1]))))
def run_encode(self, value):
"""
Verifies whether an input is valid 14-bit integer,
returning its encoded value.
Arguments:
value {byte} -- 14-bit signed integer.
Returns:
None.
"""
int_value = int.from_bytes(value, byteorder='big', signed=True)
if self.enc_range[0] <= int_value <= self.enc_range[1]:
self._encode(int_value)
print('Value {0} encodes as {1}'.format(int_value, self.enc_byte))
else:
print('Value {0} out of range: {1}'.format(int_value, self.enc_byte))
def main():
# Creater an instance of Efun().
e = Efun()
# Set strings for Argparse.
description = 'Efun is an Art + Logic Enconding + Decoding application.'
encode_help = 'Enter an INT value in the range {}.'.format(e.enc_range)
decode_help = 'Enter an HEX value in the range {}.'.format(e.dec_range)
# Run CLI menu.
parser = argparse.ArgumentParser(description=description)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-e', '--encode', type=int, help=encode_help)
group.add_argument('-d', '--decode', help=decode_help)
args = parser.parse_args()
# Get the 14-byte input, enconde/decode it, and print results.
bit_range = 14
if args.encode:
sig_bytes = args.encode.to_bytes(bit_range, byteorder='big', signed=True)
e.run_encode(sig_bytes)
else:
e.run_decode(args.decode)
if __name__ == "__main__":
main()

View file

@ -0,0 +1 @@
# -*- coding: utf8 -*-

View file

@ -0,0 +1,12 @@
[tox]
envlist = py27
[testenv]
deps = pytest
commands =
pytest
[testenv:lint]
skip_install = true
deps = flake8
commands = flake8 src/

View file

@ -0,0 +1,4 @@
__pycache__
.vercel
venv
.env

View file

@ -0,0 +1,5 @@
requirements-dev.txt
venv
main.py
README.md
.gitignore

View file

@ -0,0 +1,36 @@
# Location API 🗺
Location API built with FastAPI.
<br>
## Endpoint /location
Input data:
```
{"city": "berlin"}
```
Response:
```
{"lat":52.5186925,"lon":13.3996024,"tzone":1}
```
### Installing
```
virtualenv -p python3.9 venv
source venv/bin/activate
pipenv install
```
### Running
```
python3 main.py
```

View file

@ -0,0 +1,14 @@
from .routes import router as LocationRouter
from fastapi import FastAPI
app = FastAPI()
@app.get("/", tags=["Root"])
async def read_root() -> dict:
return {
"message": "Location API 🗺✨. Try /docs to learn about this API"
}
app.include_router(LocationRouter, prefix="/tzone")

View file

@ -0,0 +1,35 @@
import pytz
import datetime
from geopy.geocoders import Nominatim
from timezonefinder import TimezoneFinder
def _find_timezone(lat, lon) -> int:
obj = TimezoneFinder()
tzone = obj.timezone_at(lng=lon, lat=lat)
timezone = datetime.datetime.now(pytz.timezone(tzone)).strftime('%z')
try:
timezone = int(timezone[:-2])
except:
print('Error formatting timezone {}'.format(timezone))
return timezone
def get_location(data) -> dict:
try:
city = data['city']
except:
print('Data is ill-formatted: {}'.format(data))
geolocator = Nominatim(user_agent="$choices")
location = geolocator.geocode(city)
data = {
'lat': location.latitude,
'lon': location.longitude,
'tzone': _find_timezone(location.latitude, location.longitude)
}
return data

View file

@ -0,0 +1,17 @@
from pydantic import BaseModel
from fastapi import APIRouter
from .methods import get_location
router = APIRouter()
class City(BaseModel):
city: str
@router.post("/")
def search_city(data: City) -> dict:
return get_location(data.dict())

View file

@ -0,0 +1,4 @@
import uvicorn
if __name__ == "__main__":
uvicorn.run("api.api:app", host="0.0.0.0", port=8000, reload=True)

View file

@ -0,0 +1 @@
uvicorn

View file

@ -0,0 +1,4 @@
timezonefinder==5.2.0
pytz==2022.1
pydantic
fastapi

View file

@ -0,0 +1,11 @@
{
"routes": [
{"src": "/(.*)", "dest": "api/api.py"}
],
"functions": {
"api/api.py": {
"memory": 3008,
"maxDuration": 30
}
}
}

View file

@ -0,0 +1,104 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/

View file

@ -0,0 +1,4 @@
run:
python3 find_glob_boundary.py
.PHONY: run

View file

@ -0,0 +1,103 @@
#!/usr/bin/env python3
import sys
def _find_left_boundary(glob):
"""
Given a glob, search for left boundary, returning a tuple with
boundary values or None if boundary was not found.
"""
row, ncells = 0, 0
while row < len(glob):
col = 0
while col < len(glob[row]):
ncells = ncells + 1
if glob[row][col] == 1:
return (row, col), ncells
else:
col = col + 1
row = row + 1
return None, ncells
def _find_right_boundary(glob):
"""
Given a glob, search for right boundary, returning a tuple with
boundary values or None if boundary was not found.
"""
row, ncells = len(glob) - 1, 0
while row > 0:
col = len(glob[row]) - 1
while col > 0:
ncells = ncells + 1
if glob[row][col] == 1:
return (row, col), ncells
else:
col = col - 1
row = row - 1
return None, ncells
def _print_results(n_cells, top_left, bottom_right):
"""
Print results in the desired format.
"""
print("Cell Reads : {}".format(n_cells))
print("Boundary:")
print(" Top Left: {}".format(top_left))
print(" Bottom Right: {}".format(bottom_right))
def main(glob):
"""
Grab a NxN glob and print out the results.
"""
print("\nTesting {}".format(glob))
if len(glob[0]) != len(glob):
print("Matrix needs to be NxN.")
else:
top_left, nleft = _find_left_boundary(glob)
if top_left:
bottom_right, nright = _find_right_boundary(glob)
if not bottom_right:
bottom_right = top_left
_print_results(nleft + nright, top_left, bottom_right)
else:
print("Could not find left boundary, maybe there is no 1s in your glob?")
if __name__ == "__main__":
globs = []
globs.append([[0, 1]])
globs.append([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
globs.append([[0, 0], [0,0]])
globs.append([[1, 0, 0], [0, 0, 0], [0, 0, 0]])
globs.append([[0, 1, 0], [0, 0, 0], [0, 0, 0]])
globs.append([[0, 1, 1], [0, 1, 1], [0, 0, 0]])
for glob in globs:
main(glob)

View file

@ -0,0 +1,5 @@
LOG_LEVEL = INFO
LOG_FORMAT = %(levelname)s: %(message)s
BOUNDARY_LIMIT_ENC = -8192, 8191
BOUNDARY_LIMIT_DEC = 0x0000, 0x7F7F
INPUT_STREAM_FILE = './data/InputStream.txt'

71
web2-projects/magic-pen/.gitignore vendored Normal file
View file

@ -0,0 +1,71 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# pyenv,
.python-version
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# mypy
.mypy_cache/
# Others
.DS_Store
.env

View file

@ -0,0 +1,29 @@
.PHONY: setup install clean test lint run
default: test
setup:
pip install -r requirements.txt
install:
python setup.py install
run:
epen
clean:
@find . -type f -name '*.pyc' -delete
@find . -type d -name '__pycache__' | xargs rm -rf
@find . -type d -name '*.ropeproject' | xargs rm -rf
@rm -rf build/
@rm -rf dist/
@rm -rf *.egg*
@rm -f MANIFEST
@rm -rf docs/build/
@rm -f .coverage.*
test:
@tox -- -s
lint:
@tox -e lint

View file

@ -0,0 +1,144 @@
# Magic pen
This program creates an interface for a digital pen for drawing.
----
## Installing
Create and source virtual enviroment. You can use [virtualenv](https://virtualenv.pypa.io/en/latest/) or [conda](https://docs.conda.io/en/latest/):
```bash
virtualenv venv
source venv/bin/activate
```
Create and customize an enviroment file:
```bash
cp .env_example .env
vim .env
```
Install dependencies:
```bash
make setup
```
Install EPen:
```bash
make install
```
-----
## Usage
Edit the stream data to be read by the program:
```
vim data/InputStream.txt
```
Run this program with:
```bash
make run
```
or simply
```bash
epen
```
These commands run `./src/main.py`, which calls a class called `Epen()`.
This is the main class that defines all the functionalities of this application.
-----
## Examples
The stream:
```bash
F0A04000417F4000417FC040004000804001C05F205F20804000
```
prints
```bash
CLR;
CO 0 255 0 255;
MV (0, 0);
PEN DOWN;
MV (4000, 4000);
PEN UP;
```
The stream:
```bash
F0A0417F40004000417FC067086708804001C0670840004000187818784000804000
```
prints
```bash
CLR;
CO 255 0 0 255;
MV (5000, 5000);
PEN DOWN;
MV (8191, 5000);
PEN UP;
MV (8191, 0);
PEN DOWN;
MV (5000, 0);
PEN UP;
```
And the stream:
```bash
F0A040004000417F417FC04000400080400047684F5057384000804001C05F204000400001400140400040007E405B2C4000804000
```
prints
```bash
CLR;
CO 0 0 255 255;
MV (0, 0);
PEN DOWN;
MV (4000, 0) (4000, -8000) (-4000, -8000) (-4000, 0) (-500, 0);
PEN UP;
(base)
```
## Developer
### Linting
You can lint the code with:
```bash
make lint
```
### Cleaning up
Clean residual compilation and installation files with:
```bash
make clean
```
----
Thank you for reading my code!

View file

@ -0,0 +1,13 @@
# Encoding function
6111 -> 6f5f
340 -> 4254
-2628 -> 2b3c
-255 -> 3e01
7550 -> 7a7e
# Decoding function
0A0A -> -6902
0029 -> -8151
3F0F -> -113
4400 -> 512
5E7F -> 3967

View file

@ -0,0 +1,3 @@
#F0A04000417F4000417FC040004000804001C05F205F20804000
#F0A0417F40004000417FC067086708804001C0670840004000187818784000804000
F0A040004000417F417FC04000400080400047684F5057384000804001C05F204000400001400140400040007E405B2C4000804000

View file

@ -0,0 +1,4 @@
tox==3.14.3
virtualenv==20.0.1
python-dotenv==0.11.0
numpy==1.21.0

View file

@ -0,0 +1,13 @@
from setuptools import setup, find_packages
setup(
name='epen',
version='0.0.1',
packages=find_packages(),
include_package_data=True,
author='steinkirch',
entry_points='''
[console_scripts]
epen=src.main:main
''',
)

View file

@ -0,0 +1,304 @@
# -*- coding: utf8 -*-
import sys
import logging as l
import src.encoder as enc
from src.utils import get_boundary_limits
class EPen(object):
def __init__(self):
self.boundary = enc.get_boundary_limits('enc')
self.color = None
self.pen_location = None
self.is_pen_down = None
def _parse_clear(self):
"""
Runs clear operations and prints CLEAR STDOUT.
"""
self.pen_location = [0, 0]
self.color = [0, 0, 0, 255]
self.is_pen_down = False
print('CLR;')
def _parse_pen_state(self, substr):
"""
Sets pen to be UP or DOWN and prints PEN STATE STDOUT.
Arguments:
substr {str} -- parameters for this opcode, which is
either 0 (for pen up) or any other (for pen down)
"""
if substr:
pen_state = enc.run_decoder(substr)
else:
l.error('Could not parse pen state string {}'.format(substr))
if pen_state == 0:
if self.is_pen_down:
self.is_pen_down = False
print('PEN UP;')
else:
if not self.is_pen_down:
self.is_pen_down = True
print('PEN DOWN;')
def _parse_color(self, substr):
"""
Sets pen color and prints COLOR SET STDOUT.
Arguments:
substr {str} -- RGBA parameters for this opcode,
which is represented by four integers in the range
[0, 255] defining colors (red, green, blue) and alpha.
"""
red = enc.run_decoder(substr[:4])
green = enc.run_decoder(substr[4:8])
blue = enc.run_decoder(substr[8:12])
alpha = enc.run_decoder(substr[12:16])
self.color = [red, green, blue, alpha]
print('CO {0} {1} {2} {3};'.format(*self.color))
def _parse_pen_move(self, substr):
"""
Parses a substring of parameters and usex this data
as pair of coordinates to move the pen inside the
square boundary limit.
Arguments:
substr {str} -- (x,y) pairs with directions to where
the pen should move to (at least one pair).
Returns:
i - an integer indicating how much of the input
substring was used in this opcode.
"""
i = 0
move_list = []
while i < len(substr):
try:
# Ends if find another opcode.
if substr[i:i+2] in set(['F0', '80', 'A0']):
break
if len(substr[i:]) > 8:
x_coords = enc.run_decoder(substr[i:i+4])
y_coords = enc.run_decoder(substr[i+4:i+8])
move_list.append([x_coords, y_coords])
i += 8
else:
break
except (KeyError, ValueError) as e:
l.error('Ill-formated MV data: {}'.format(e))
self._move_pen(move_list)
return i
def _print_mv(self, cmd_list=None):
"""
Prints MV PEN STDOUT. If a string with a list of commands
is given, print that instead of pen_location.
Arguments:
cmd_list {list (optional)} -- a list with pairs of
(x, y) coordinates.
"""
if cmd_list:
cmd_str = ' '.join(cmd_list)
print('MV {};'.format(cmd_str))
else:
print('MV ({0}, {1});'.format(self.pen_location[0], self.pen_location[1]))
def _fix_boundary(self, coord):
"""
Check if the coordinate exceeds
the boundary limit, returning this
value in that case.
Arguments:
coord {int} -- x or y coordinate.
Returns:
An integer representing the fixed coordinate.
"""
if coord < self.boundary[0]:
return self.boundary[0]
elif coord > self.boundary[1]:
return self.boundary[1]
else:
return coord
def _fix_boundaries(self, x, y):
"""
Checks if a set of (x, y) coordinates
exceeds the boundary limits, returning
a fixed (x, y) pair.
Arguments:
coord {list} -- (x, y) coordinates.
Returns:
A (x, y) pair representing the fixed coordinates.
"""
return self._fix_boundary(x), self._fix_boundary(y)
def _is_inside_boundary(self, xymove=None):
"""
Checks whether a set of coordinate is inside
of the boundary, returning True if the case,
or False otherwise.
Arguments:
xymove {tuple} - (x, y) tuple.
Returns:
True if the coordinates were fixed, False
otherwise.
"""
if xymove is None:
xymove = self.pen_location
x_fix = self._fix_boundary(xymove[0])
y_fix = self._fix_boundary(xymove[1])
return (xymove[0] == x_fix and xymove[1] == y_fix)
def _is_on_the_boundary(self, xymove=None):
"""
Checks whether a coordinate is on the boundary,
returning True if the case, or False otherwise.
Arguments:
xymove {tuple} - (x, y) tuple.
Returns:
True if any of the (x, y) coordinates are
on the boundary, False otherwise.
"""
if xymove is None:
x, y = self.pen_location
return x == self.boundary[0] or x == self.boundary[1] or \
y == self.boundary[0] or y == self.boundary[1]
def _get_next_position(self, xymove):
"""
Givens a move coordinates (x, y), returns the next
destination coordinate for the pen.
Arguments:
xymove {tuple} - (x, y) tuple.
Returns:
The next (x, y) coordinates to move the pen to.
"""
return self.pen_location[0] + xymove[0], \
self.pen_location[1] + xymove[1]
def _move_pen(self, move_list):
"""
Changes the location of the pen relative to its current location.
Arguments:
move_list {list} -- list of coordinates to move the pen.
"""
# Loop over the list of (x ,y) moves, adding them to
# a dictionary, together with a boolean False if x or y
# is outside the limits.
tablet_dict = {}
for n, xymove in enumerate(move_list, 1):
self.pen_location = self._get_next_position(xymove)
if self._is_inside_boundary():
tablet_dict[n] = [self.pen_location, True]
else:
tablet_dict[n] = [self.pen_location, False]
# Find the positions and print it as pen down.
if self.is_pen_down:
cmd_list = []
for i in sorted(tablet_dict):
x = tablet_dict[i][0][0]
y = tablet_dict[i][0][1]
is_inside = tablet_dict[i][1]
if is_inside:
cmd_list.append('({0}, {1})'.format(x, y))
else:
break
if cmd_list:
self._print_mv(cmd_list)
# It broke the boundary at some point.
if i < len(tablet_dict):
for item in range(i, len(tablet_dict)+1):
coords, is_inside = tablet_dict[item]
self.pen_location = self._fix_boundaries(coords[0], coords[1])
self._print_mv()
if item != len(tablet_dict):
if not is_inside:
if self.is_pen_down:
self._parse_pen_state('4000')
else:
self._parse_pen_state('4001')
else:
coords, is_inside = tablet_dict[i]
self.pen_location = self._fix_boundaries(coords[0], coords[1])
if not is_inside:
self._parse_pen_state('4000')
# Pen is up.
else:
last_position = tablet_dict[sorted(tablet_dict)[-1]][0]
self.pen_location = self._fix_boundaries(*last_position)
self._print_mv()
def parse_stream(self, stream_str):
"""
Parses a stream string and run its commands and their
adjacent parameters.
Arguments:
stream_str {str} - data stream of actions be taken.
"""
i = 0
while i < len(stream_str):
# Opcode is CLEAR.
if stream_str[i:i+2] == 'F0':
l.debug('Parsing clear opcode...')
self._parse_clear()
i += 2
# Opcode is SET PEN STATE.
elif stream_str[i:i+2] == '80':
l.debug('Parsing pen state opcode...')
self._parse_pen_state(stream_str[i+2:i+6])
i += 6
# Opcode is SET COLOR.
elif stream_str[i:i+2] == 'A0':
l.debug('Parsing color opcode...')
self._parse_color(stream_str[i+2:i+18])
i += 18
# Opcode is MOVE PEN.
elif stream_str[i:i+2] == 'C0':
l.debug('Parsing pen move opcode...')
i += 2 + self._parse_pen_move(stream_str[i+2:])
# Opcode is not identified.
else:
l.debug('Non-identificated char: {}'.format(stream_str[i]))
i += 1

View file

@ -0,0 +1,2 @@
# -*- coding: utf8 -*-

View file

@ -0,0 +1,130 @@
# -*- coding: utf8 -*-
import logging as l
from src.utils import get_boundary_limits
def decoder(higher_byte, lower_byte):
"""
Converts encoded hex value back into the original signed integer.
Argument:
higher_byte {string} -- higher byter hex input to be decoded.
lower_byte {string} -- lower byter hex input to be decoded.
Returns:
Decoded value string in decimal base.
"""
min_lim, _ = get_boundary_limits('enc')
# Left shift 7 to get higher byte.
higher_byte <<= 7
# 'OR' byte withing the decimal lower limit.
encoding_range = get_boundary_limits
return (lower_byte | higher_byte) + min_lim
def encoder(value):
"""
Gets a signed integer and return a 4 character string,
such that after the encoding is complete, the most
significant bit of each byte has been cleared.
Argument:
value {string} -- integer input value to be encoded.
Returns:
Encoded 2-byte string.
"""
# Add 8192 for unsigned
_, max_lim = get_boundary_limits('enc')
sign_int = int(value)
unsign_int = sign_int + max_lim + 1
# 'AND' with 0b0000000001111111 for lower byte and
# 'AND' with 0b0011111110000000 for higher byter
lower_byte = unsign_int & 0x007F
higher_byte = ((unsign_int & 0x3F80) << 1)
# Return hex encoding of the sum of the two bytes
return hex(lower_byte + higher_byte)
def extract_two_bytes(value):
"""
Extract two bytes from a hexadecimal string.
Arguments:
value {hex} -- hexadecimal between 0x0000 and 0x7F7F.
Returns:
higher_byte, lower_byte -- byte strings.
"""
# 'And' with 0b0000000001111111 for lower byte
lower_byte = value & 0x00FF
# Left shift 8 positions for higher byte
higher_byte = value >> 8
return higher_byte, lower_byte
def run_decoder(input_hex):
"""
Verifies if an input is valid 2-byte hexadecimal,
returning its decoded value.
Arguments:
value {string} -- hexadecimal input value.
Returns:
Decoded byte or -1 (if error).
"""
min_lim, max_lim = get_boundary_limits('dec')
# Convert to hexadecimal.
try:
l.debug('Converting {} to decimal...'.format(input_hex))
input_dec = int(input_hex, 16)
except ValueError as e:
l.error('Could not convert {0} decimal: {1}'.format(input_hex, e))
return -1
# Verify whether the input is within the range,
# and then decode it.
if min_lim <= input_dec <= max_lim:
dec_byte = decoder(*extract_two_bytes(input_dec))
l.debug('{0} decodes as {1}'.format(input_dec, dec_byte))
return dec_byte
else:
l.debug('{0} is out of range of [{1}, {2}].'.format(input_dec,
hex(min_lim), hex(max_lim)))
return -1
def run_encoder(value):
"""
Verifies whether a byte input is a valid 14-bit integer,
returning its encoded value.
Arguments:
value {byte} -- 14-bit signed integer.
Returns:
Encoded byte or -1 (if error).
"""
int_value = int.from_bytes(value, byteorder='big', signed=True)
min_lim, max_lim = get_boundary_limits('enc')
if min_lim <= int_value <= max_lim:
enc_byte = encoder(int_value)
l.debug('Value {0} encodes as {1}'.format(value, enc_byte))
return enc_byte
else:
l.error(int('Value {0} out of range: {1}'.format(value, enc_byte)))
return -1

View file

@ -0,0 +1,28 @@
#!/usr/bin/env python
import logging as l
import src.Epen as Epen
import src.utils as utils
from src.settings import LOG_LEVEL, LOG_FORMAT, INPUT_STREAM_FILE
# Define log level: choose between DEBUG or INFO.
l.basicConfig(level=LOG_LEVEL, format=LOG_FORMAT)
def main():
p = Epen.EPen()
# Get input stream from file.
input_stream = utils.parse_input_stream(INPUT_STREAM_FILE)
# Extract and run list of commands from stream input.
if input_stream:
p.parse_stream(input_stream)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,16 @@
# Load all env variables from .env file
import os
from dotenv import load_dotenv
from pathlib import Path
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)
# General constants
LOG_LEVEL = os.getenv('LOG_LEVEL')
LOG_FORMAT = os.getenv('LOG_FORMAT')
BOUNDARY_LIMIT_ENC = os.getenv('BOUNDARY_LIMIT_ENC')
BOUNDARY_LIMIT_DEC = os.getenv('BOUNDARY_LIMIT_DEC')
INPUT_STREAM_FILE = os.getenv('INPUT_STREAM_FILE')

View file

@ -0,0 +1,54 @@
# -*- coding: utf8 -*-
import os
import sys
import logging as l
from src.settings import BOUNDARY_LIMIT_ENC, BOUNDARY_LIMIT_DEC
def parse_input_stream(filename):
"""
Loads a file with the input stream with
encoded commands.
Returns:
filename -- location of the file in disk.
"""
try:
with open(filename, 'r') as f:
lines = f.readlines()
for input_stream in lines:
if input_stream[0] != '#':
l.debug('Input stream:\n{}'.format(input_stream))
return input_stream.strip()
except (KeyError, OSError, TypeError) as e:
l.error('Could not open input stream file {0}: {1}.'.format(filename, e))
sys.exit(0)
def get_boundary_limits(lim_base):
"""
Extract boundary limits from env file, returning two integers
representing these limits, either in the encoded or
the decode base.
Arguments:
lim_base {string}: 'enc' for the encoded limit
representation or 'dec' for the encoded limit
representation.
"""
try:
if lim_base == 'enc':
enc_range = tuple(BOUNDARY_LIMIT_ENC.split(', '))
return int(enc_range[0]), int(enc_range[1])
elif lim_base == 'dec':
dec_range = tuple(BOUNDARY_LIMIT_DEC.split(', '))
return int(dec_range[0], 16), int(dec_range[1], 16)
except (KeyError, ValueError, AttributeError) as e:
l.error('Could not extract boundary limits from .env file: {}'.format(e))
return -1

View file

@ -0,0 +1,12 @@
[tox]
envlist = py27
[testenv]
deps = pytest
commands =
pytest
[testenv:lint]
skip_install = true
deps = flake8
commands = flake8 src/

View file

View file

@ -0,0 +1,62 @@
# Follower Maze Code Challenge Instructions
Follower-Maze is a **client-server application** built for social networking.
Users follow other users, post messages, send private messages, unfollow users, and so on.
* Each of these actions is an _event_, created by an _event-source_ and sent to the _server_.
* Each event is processed by the _server_.
* The processing of the event may have follow-on effects on other _user-clients_.
The **server** listens for two kinds of **clients**:
* **1 event source**, which emits events.
* **N user-clients** that connect to the server.
#### Client/Server Protocol
* The protocol is line-based i.e. a LF control character terminates each message.
All strings are encoded in UTF-8.
* Data is transmitted over TCP sockets.
* The _event source_ connects on port 9090 and will start sending events as soon as the connection is accepted.
* The _user clients_ connect on port 9099 and identify themselves with their user ID.
For example, once connected a _user client_ may send down `2932\r\n`,
indicating that it is representing user 2932.
* After the identification is sent,
the user client starts waiting for events to be sent to them by the server.
#### Events
There are five possible events.
The table below describes payloads sent by the event source and what each represents:
```
| Payload | Sequence # | Type | From User Id | To User Id |
|---------------|------------|---------------|--------------|------------|
| 666|F|60|50 | 666 | Follow | 60 | 50 |
| 1|U|12|9 | 1 | Unfollow | 12 | 9 |
| 542532|B | 542532 | Broadcast | - | - |
| 43|P|32|56 | 43 | Private Msg | 32 | 56 |
| 634|S|32 | 634 | Status Update | 32 | - |
```
#### Event Rules
* Each message begins with a sequence number.
* However, the event source _does not send events in any given order_.
In particular, sequence number has no effect on the order in which events are sent.
* Events _may_ generate notifications for _user clients_.
If there is a respective target _user client_ connected,
the notification is routed according to the following rules:
* Follow: Only the `To User Id` is notified
* A follow event from user A to user B means that user A now follows user B.
* Unfollow: No clients are notified
* An unfollow event from user A to user B means that user A stopped following user B.
* Broadcast: All connected _user clients_ are notified
* Private Message: Only the `To User Id` is notified
* Status Update: All current followers of the `From User ID` are notified
* If there are no _user clients_ connected for a user,
any notifications for them are currently ignored.
_User clients_ are notified of events _in the correct sequence-number order_,
regardless of the order in which the _event source_ sent them.

View file

@ -0,0 +1,49 @@
"""
Interview note:
This is a client "stub", here for demonstration and documentation
purposes only.
In a real production environment, this client would send requests over
the network to a product detail backend service.
The product detail backend service is responsible for retrieving product
detail records from its datastore (e.g., a mysql database), and
returning them to callers.
"""
class ProductDetailClient:
def lookup(product_token):
"""
Given a product token, makes a blocking call to the lookup endpoint of
the product detail backend service,
and returns a corresponding product detail record, if found.
If no record is found, returns None.
Example result:
{"token": "AAA",
"description": "Red Bike",
"price_cents": 45000}
"""
pass
def batch_lookup(product_tokens):
"""
Given a list of product tokens, makes a blocking call to the
batch lookup endpoint of the product detail backend service,
and returns a map of product token to product detail record, for all
that are found.
Any records that cannot be found are ommitted from the result.
Example result:
{"AAA": {
"token": "AAA",
"description": "Red Bike",
"price_cents": 45000},
"BBB": {
"token": "BBB",
"description": "Blue Bike",
"price_cents": 37500}}
"""
pass

View file

@ -0,0 +1,44 @@
"""
Interview note:
This is a client "stub", here for demonstration and documentation
purposes only.
In a real production environment, this client would send requests over
the network to a product search backend service.
The product search backend service is responsible for performing lookups
against a product search index (e.g., elasticsearch) and returning product
tokens that are search result "hits".
"""
class ProductSearchClient:
def search_for_all_in_price_range(lower_cents, upper_cents, start_record, num_records):
"""
Given a price range plus pagination information,
returns a list of product tokens of products
whose price falls in the range.
The price range is inclusive - that is,
a given price "p" matches if:
upper_cents >= p >= lower_cents
If no products have prices in the price range,
returns an empty list.
Product tokens are ordered by price, from lowest to highest.
Out of the total list of possible product tokens,
only the "page" of product tokens starting at the list position
start_record, and ending at start_record+num_records, are returned.
If start_record+num_records is greater than the number of actual
result records, the resulting list size will be (accordingly)
smaller than num_records.
Example result:
["AAA",
"BBB"]
"""
pass

View file

@ -0,0 +1,43 @@
import re
TOKEN_REGEX = re.compile("^[0-9A-F-]+$")
class Service:
def __init__(self, product_detail_client, product_search_client):
self.product_detail_client = product_detail_client
self.product_search_client = product_search_client
def lookup(self, product_token):
"""
Given a valid product token,
lookup product detail information from the product detail backend service.
- Results in Bad Request if the token is invalid
- Results in Not Found if the product is unknown to the backend service
"""
if not TOKEN_REGEX.match(product_token):
return {"status": 400}
lookup_result = self.product_detail_client.lookup(product_token)
if lookup_result:
return {"status": 200, "product": lookup_result}
else:
return {"status": 404}
def search_by_price_range(self, lower_cents, upper_cents, start_record, num_records):
page_of_product_tokens = \
self.product_search_client.search_for_all_in_price_range(
lower_cents,
upper_cents,
start_record,
num_records)
product_detail_results = []
for product_token in page_of_product_tokens:
product_detail_results.append(self.lookup(product_token)["product"])
return {"status": 200, "products": product_detail_results}
if __name__ == "__main__":
a = Service()
print(a.lookup('aaa'))

View file

@ -0,0 +1,95 @@
import os
import sys
import unittest
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../src"))
from service import Service
class ProductDetailClientTestDouble:
def __init__(self):
self.contents = {}
def lookup(self, product_token):
if product_token in self.contents:
return self.contents[product_token]
else:
return None
class ProductSearchClientTestDouble:
def __init__(self):
self.search_to_results = {}
def search_for_all_in_price_range(self, lower_cents, upper_cents, start_record, num_records):
return self.search_to_results[(lower_cents, upper_cents, start_record, num_records)]
class ServiceTest(unittest.TestCase):
def setUp(self):
self.detail_client = ProductDetailClientTestDouble()
self.search_client = ProductSearchClientTestDouble()
self.service = Service(self.detail_client, self.search_client)
def test_lookup_success(self):
expected_product = {
"token": "AAA",
"description": "Red Bike",
"price_cents": 45000
}
self.detail_client.contents["AAA"] = expected_product
self.assertEqual(
self.service.lookup("AAA"),
{"status": 200,
"product": expected_product})
def test_lookup_not_found(self):
self.assertEqual(
self.service.lookup("AAA"),
{"status": 404})
def test_lookup_bad_input(self):
self.assertEqual(
self.service.lookup("this-is-not-a-uuid"),
{"status": 400})
def test_search_price_range(self):
brown_bike = {
"token": "AAA",
"description": "Brown Bike",
"price_cents": 10000
}
blue_bike = {
"token": "BBB",
"description": "Blue Bike",
"price_cents": 37500
}
red_bike = {
"token": "CCC",
"description": "Red Bike",
"price_cents": 45000
}
gray_bike = {
"token": "DDD",
"description": "Gray Bike",
"price_cents": 99900
}
self.detail_client.contents["AAA"] = brown_bike
self.detail_client.contents["BBB"] = blue_bike
self.detail_client.contents["CCC"] = red_bike
self.detail_client.contents["DDD"] = gray_bike
self.search_client.search_to_results[(30000, 50000, 0, 10)] = ["BBB", "CCC"]
self.assertEqual(
self.service.search_by_price_range(30000, 50000, 0, 10),
{"status": 200,
"products": [blue_bike, red_bike]})
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1 @@
## Parsing Medium posts

View file

@ -0,0 +1,121 @@
import json
def solution(postContentString, deltasString):
postContent = json.loads(postContentString)
deltas = json.loads(deltasString)
# -------------------------------------
# Loop to each delta action from deltas.
# -------------------------------------
for delta in deltas:
# ----------------------
# Get delta action data.
# ----------------------
try:
delta_type = delta['type']
delta_pindex = delta['paragraphIndex']
# ---------------------------------------
# Check if delta paragraph index is valid.
# ---------------------------------------
if delta_pindex < 0 or delta_pindex >= len(postContent['paragraphs']):
continue
except (KeyError, IndexError) as e:
continue
# ------------------------------
# Execute update paragraph delta.
# ------------------------------
if delta_type == 'updateParagraph':
try:
delta_paragraph_text = delta['paragraph']['text']
postContent['paragraphs'][delta_pindex]['text'] = delta_paragraph_text
except IndexError:
continue
# ----------------------------
# Execute add paragraph delta.
# ----------------------------
elif delta_type == 'addParagraph':
try:
delta_paragraph = delta['paragraph']
postContent['paragraphs'].insert(delta_pindex, delta_paragraph)
# ------------------
# Fix section index.
# ------------------
i = delta_pindex
while i < len(postContent['sections'][delta_pindex:]) + 1:
postContent['sections'][i]['startIndex'] += 1
i += 1
except IndexError:
continue
# -------------------------------
# Execute delete paragraph delta.
# -------------------------------
elif delta_type == 'deleteParagraph':
try:
postContent['paragraphs'].pop(delta_pindex)
# ------------------
# Fix section index.
# ------------------
# Fix section indexes after removal.
i = delta_pindex
while i < len(postContent['sections'][delta_pindex:]) + 1:
postContent['sections'][i]['startIndex'] -= 1
i += 1
# Remove any empty section.
i = 1
for i, section in enumerate(postContent['sections']):
if section['startIndex'] <= 0:
postContent['sections'].pop(i)
except IndexError:
continue
# ------------------
# Print out results.
# ------------------
try:
# -------------------------------------------
# Extract sections and paragraph, after delta.
# -------------------------------------------
sections = postContent['sections']
paragraphs = postContent['paragraphs']
# ------------------------
# Find all section indexes.
# ------------------------
section_indexes = set()
for section in sections:
section_indexes.add(section['startIndex'])
# ---------------------------------------------------
# Loop over paragraphs to create the resulting string.
# ---------------------------------------------------
res = []
for i, paragraph in enumerate(paragraphs, start=1):
if i == len(paragraphs):
res.append(f'{paragraph["text"]}')
elif i in section_indexes:
res.append(f'{paragraph["text"]}\n-\n')
else:
res.append(f'{paragraph["text"]}\n')
except (KeyError, ValueError) as e:
print(f'Error: Input data is ill-formatted: {e}')
return ''.join(res)

View file

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

View file

@ -0,0 +1,25 @@
# Prototyping a storytelling NFT
For dev purpose only: the NFT drop is actually created on-chain.
---
### Running
Create and edit an `.env` file:
```
cp sample_env .env
```
Create an virtual env and install requirements:
```
pip3 install -r requirements.txt
```
Run:
```
python3 gen_nfts.py
```

View file

@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
import os
import sys
import random
from pathlib import Path
from dotenv import load_dotenv
from PIL import Image, ImageDraw, ImageFont
def create_dest_dir(final_dir):
"""Crete a destination dir for NFTs"""
try:
os.mkdir("nfts")
except FileExistsError:
return
def get_config(key, env_path=None):
"""Given a key, get the value from the env file.
Arguments:
key (str)
Return:
value(str)
"""
env_path = env_path or Path('.') / '.env'
load_dotenv(dotenv_path=env_path)
value = os.getenv(key)
if not value:
print('📛 Please set {} in .env'.format(key))
sys.exit(0)
return value
def open_trait_file(filename):
"""Open a file with a list of trait strings and return that list.
Arguments:
filename (str) - file to be open
Return:
items(list) - content of the file
"""
items = []
try:
with open(filename, "r") as f:
items = f.readlines()
items = [line.rstrip('\n') for line in items]
except IOError:
print("Could not open {}".format(filename))
sys.exit(1)
return items
def save_image(nft_num, this_nft):
"""Save the the list of strings in a NFT into an image.
Arguments:
nft_num(str) - this NFT id
this_nft(str) - the content of this nft
"""
img = Image.new('RGB', (600, 600), color=(255, 255, 255))
d = ImageDraw.Draw(img)
fnt = ImageFont.truetype("Roboto-Medium.ttf", 30)
d.text((20, 150), this_nft, font=fnt, fill=(0, 0, 0))
d.text((550, 30), "#{}".format(str(nft_num)), font=fnt, fill=(0, 0, 0))
try:
nft_name = "nft_{}.png".format(str(nft_num))
img.save(nft_name)
except:
print("Could not save {0} with {1}".format(nft_num, this_nft))
def get_traits_list(num_trait_lines):
"""Return a multi-dimensional array with every trait list.
Arguments:
num_trait_lines(str) - the number of trait lines in one nft
Returns:
the final string for NFT
"""
trait_file_str = get_config("TRAIT_FILE_STR")
traits_list = []
for n in range(int(num_trait_lines)):
trait_file = trait_file_str.format(n)
traits_list.append(open_trait_file(trait_file))
return traits_list
def get_this_nft(num_trait_lines, traits_list):
""" Get the random content of one NFT.
Arguments:
num_trait_lines (str) - the number of trait lines in one nft
traits_list (list) - multi-dimensional list with all traits
Return:
this_nft (list) - what composes the current NFT
"""
this_nft = []
for n in range(int(num_trait_lines)):
this_trait_list = traits_list[n]
random_trait_n = random.randint(0, len(this_trait_list)-1)
random_trait_str = this_trait_list[random_trait_n]
this_nft.append(random_trait_str)
return "\n".join(this_nft)
def generate_nfts(num_edition, num_trait_lines, traits_list):
"""Create and save NFT images
Arguments:
num_edition(str) - num of NFTs to be generated
num_trait_lines (str) - the number of trait lines in one nft
traits_list (list) - multi-dimensional list with all traits
"""
for n in range(1, int(num_edition) + 1):
this_nft = get_this_nft(num_trait_lines, traits_list)
save_image(n, this_nft)
def main():
num_trait_lines = get_config("NUM_TRAIT_LINES")
final_dir = get_config("FINAL_DIR")
num_edition = get_config("NUM_EDITION")
create_dest_dir(final_dir)
traits_list = get_traits_list(num_trait_lines)
generate_nfts(num_edition, num_trait_lines, traits_list)
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,3 @@
pillow
python-dotenv
pathlib

View file

@ -0,0 +1,4 @@
NUM_TRAIT_LINES = 6
TRAIT_FILE_STR = "./{}.txt"
FINAL_DIR = "nfts"
NUM_EDITION = 10

View file

@ -0,0 +1,8 @@
Drama
Comedy
Action
Fantasy
Horror
Romance
Western
Thriller

View file

@ -0,0 +1,10 @@
Cape Town
Los Angeles
Prague
Lisbon
Paris
Seatle
London
Berlin
New York City
New Orleans

View file

@ -0,0 +1,9 @@
Robert Downey, Jr.
Samuel L. Jackson
Scarlett Johansson
Chris Hemsworth
Chris Evans
Zoe Saldana
Chris Pratt
Tom Hanks
Vin Diesel

View file

@ -0,0 +1,8 @@
INT. Hallway
INT. Bedroom
INT. Park
INT. Office
INT. Kitchen
EXT. Park
EXT. Lake
EXT. Highway

View file

@ -0,0 +1,6 @@
Red Dragon
Blackmagic
Hasselblade
Canon Mark V
Panasonic Lumix
ARRI Alexa

View file

@ -0,0 +1,4 @@
popcorn
diet coke
cotton candy
bubble gum

View file

@ -0,0 +1,13 @@
## Running
```
chmod a+x <filename.py>
./<filename.py>
```
or
```
python3 <filename.py>
```

View file

@ -0,0 +1,73 @@
#!/usr/bin/env python3
'''
We wish to compute the total amount of water that will be held by some
topography when an infinite amount of evenly-distributed water is poured
on it from above. The topography is represented by a sequence of integers
describing the height of each unit-width section of terrain, from left to
right. Your solution will return the total amount of width-times-height
retained by the terrain. You may assume that heights are non-negative integers.
Solution by bt3gl
'''
def _create_wall_array(size) -> list:
return [None for _ in range(size)]
def _get_tallest_wall(wall1, wall2) -> int:
return (max(wall1, wall2))
def _get_water_filling(left_wall, right_wall, middle) -> int:
return min(left_wall, right_wall) - middle
def maxWater(arr) -> int:
"""
Return the maximum water that can be stored
Input: arr (an array of positive integers)
Output: res (a positive integer)
"""
res = 0
n = len(arr)
left_wall = _create_wall_array(n)
right_wall = _create_wall_array(n)
left_wall[0], right_wall[n - 1] = arr[0], arr[n - 1]
# Come from left
for i in range(1, n):
left_wall[i] = _get_tallest_wall(left_wall[i - 1], arr[i])
# Come from right
for i in range(n - 2, -1, -1):
right_wall[i] = _get_tallest_wall(right_wall[i + 1], arr[i]);
# Get water inside
for i in range(n):
res += _get_water_filling(left_wall[i], right_wall[i], arr[i])
return res
# Test 1
arr1 = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
print(f'Solution for {arr1} is {maxWater(arr1)} (and should be 6)')
# Test 2
arr2 = [2, 0, 2]
print(f'Solution for {arr2} is {maxWater(arr2)} (and should be 2)')
# Test 3
arr3 = [3, 0, 2, 0, 4]
print(f'Solution for {arr3} is {maxWater(arr3)} (and should be 7)')
# Test 4
arr4 = [0, 0, 0, 0]
print(f'Solution for {arr4} is {maxWater(arr4)} (and should be 0)')
# Test 5
arr5 = [5, 5, 5]
print(f'Solution for {arr5} is {maxWater(arr5)} (and should be 0)')

View file

@ -0,0 +1,69 @@
#!/usr/bin/env python3
'''
We wish to compute the total amount of water that will be held by some
topography when an infinite amount of evenly-distributed water is poured
on it from above. The topography is represented by a sequence of integers
describing the height of each unit-width section of terrain, from left to
right. Your solution will return the total amount of width-times-height
retained by the terrain. You may assume that heights are non-negative integers.
Solution by bt3gl
'''
def maxWater(arr) -> int:
"""
Return the maximum water that can be stored
Input: arr (an array of positive integers)
Output: res (a positive integer)
"""
res = 0
n = len(arr)
for i in range(n):
# Define wall here
left_wall = arr[i]
right_wall = arr[i]
# Find tallest walls
for j in range(i):
if left_wall < arr[j]:
left_wall = arr[j]
for j in range(i + 1, n):
if right_wall < arr[j]:
right_wall = arr[j]
# Find shortest between tallest walls to get how much is
# held and remove blocks in the middle from the some
if right_wall < left_wall:
res += right_wall - arr[i]
else:
res += left_wall - arr[i]
return res
# Test 1
arr1 = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
print(f'Solution for {arr1} is {maxWater(arr1)} (and should be 6)')
# Test 2
arr2 = [2, 0, 2]
print(f'Solution for {arr2} is {maxWater(arr2)} (and should be 2)')
# Test 3
arr3 = [3, 0, 2, 0, 4]
print(f'Solution for {arr3} is {maxWater(arr3)} (and should be 7)')
# Test 4
arr4 = [0, 0, 0, 0]
print(f'Solution for {arr4} is {maxWater(arr4)} (and should be 0)')
# Test 5
arr5 = [5, 5, 5]
print(f'Solution for {arr5} is {maxWater(arr5)} (and should be 0)')