FACEHARD — native Python port, verbatim clone + CLI
A faithful Python reimplementation of FACEHARD 8.0, Nathan Okun's face-hardened naval armour penetration program (QuickBASIC, 2018). It reproduces both the program's numbers and its exact text output, validated against the generated BASIC working oracle (fh_oracle) by differential fuzzing. Intentional oracle corrections are documented separately.
Two ways to use it: a modern CLI (facehard run/pen/list) and a byte-faithful clone of the original interactive program (facehard emulate). Stdlib-only, pipx-installable.
Versions. The package is versioned independently of the model: this is package 1.0.0, reproducing Okun's FACEHARD 8.0 (8 August 2018). facehard.MODEL_VERSION and facehard --version both report the model version alongside the package version.
Source. Okun's original programs, papers and armour data are published at NavWeaps — The Nathan Okun Collection.
Install
pipx install facehard
facehard --version
CLI
facehard interactive wizard (modern menus)
facehard emulate the ORIGINAL FACEHARD interactive session, verbatim
facehard run [options] one impact; --output narrative|limits|plug|metrics|json|all
facehard pen [options] penetration thickness at a striking velocity
facehard list armors|nations|projectiles [--nation N]
run/pen scenario flags: --armor 1-25 --nation 1-8 --proj N -d/--diameter -w/--weight -b/--body-weight -v/--velocity -o/--obliquity; backing --wood --cement --metal --metal-type --metal-plates; nose loss --remove {none,cap,windscreen,caphead} --windscreen-wt --caphead-wt. run also takes -t/--thickness (the plate to shoot); pen omits it because it computes the thickness defeated.
Library API
from facehard import calc, results, render_results, penetration, names
calc(armor=14, nation=1, proj=18, TA=16, D=16, WT=2700, WB=2048, OB=30).vltru
results(armor=14, nation=1, proj=18, TA=12, D=16, WT=2700, WB=2048, OB=30, VS=2200)
render_results(armor=14, nation=1, proj=18, TA=12, D=16, WT=2700, WB=2048,
OB=30, VS=2200, show_limits=True) # verbatim BASIC text
penetration(armor=14, nation=1, proj=18, D=16, WT=2700, WB=2048, V=2500, OB=0) # 29.5 in
Every numeric menu choice also has a named IntEnum — drop-in for the plain numbers, which stay valid everywhere:
from facehard import penetration, Armor, Nation, USProjectile, projectiles
Nation.UK == 2 # original menu numbers, unchanged
penetration(armor=Armor.BRITISH_CA, nation=Nation.USA,
proj=USProjectile.APC_6IN_MK35_16IN_MK8, # 16-in Mk 8 (IOWA)
D=16, WT=2700, WB=2048, V=2500, OB=0)
projectiles(Nation.JAPAN).APC_TYPE91 # per-nation projectile menus
Armor.JAPANESE_VH.menu_text # the original menu line
Nation, Armor, BackingMetal (the metal_type parameter), and one projectile enum per nation (USProjectile, BritishProjectile, GermanProjectile, FrenchProjectile, ItalianProjectile, JapaneseProjectile, AustroHungarianProjectile, RussianProjectile). The short names summarise menu lines that often cover several marks — member.menu_text is authoritative.
All of calc/results/render_results accept backing, nose-covering loss (remove, windscreen_wt, caphead_wt), and manual armour overrides (armor_overrides={"Q": 0.8, "UB": 60, ...} for UB/Q/QDAM/CARTWL/CMPND/THNCHL/SOFTSHAT/THKTHN).
Coverage — the whole program
| Layer | Status |
|---|---|
| Ballistic limits (N1–N4, H1–H4) | ported, 100% vs BASIC |
| Post-impact results (outcome, exit angle, plug weights, remaining velocity, effective BL) | ported, 100% |
Verbatim results narrative (RESULTSPRINT + damage flags) |
ported, word-for-word |
| Backing (wood/cement/metal) & nose-covering loss (decapping) | ported, 100% |
| Manual armour-parameter overrides ("modify parameters" screen) | ported, applies + verbatim |
| Interactive shell (menus, info pages, prompts, re-run state) | ported, verbatim (facehard emulate) |
Not reproduced: nothing functional — only the DOS graphics-mode screen clears (cosmetic) differ.
Fidelity — how close to the BASIC?
precision_audit.py (1500 fuzzed examples, deterministic):
- Zero velocity deviations across 6,818 ballistic-limit and post-impact velocity comparisons.
- Exit angles agree exactly in the retained 10,000-case differential campaign.
- The remaining differences are BASIC display rounding of plug weights to one decimal place (maximum 0.05 lb).
The generated working oracle uses double precision and contains the documented post-impact-limit correction in ORACLE_PATCHES.md.
Verbatim-text fuzzers confirm the same at the string level: fuzz_narrative.py 99.97% of result lines, fuzz_emulate.py 99.98% of full-session lines, fuzz_override.py 100% of override sessions — residual diffs are those same sub-unit display roundings.
Layout
| Path | Role |
|---|---|
facehard/ |
installable package: model.py, emulate.py, cli.py, names.py |
fh_oracle |
compiled BASIC oracle; rebuild via build_qb64.py + QB64-PE |
facehard.bas / build_qb64.py |
the merged BASIC and its generator |
ORACLE_PATCHES.md |
intentional model corrections applied to the generated oracle |
facehard_run.py |
drives fh_oracle, parses its output |
facehard_native.py |
back-compat shim → facehard.model |
validate_native.py / validate_results.py |
limit & post-impact sweeps vs BASIC |
precision_audit.py |
exact deviation audit (identical / off-by-1 / ≥2) |
fuzz_native/narrative/emulate/override.py |
differential fuzzers |
reproduce_table4_native.py |
reproduces the published comparison table, pure Python |
Licence
Not open-source. The penetration mechanics and associated IP — including the
formulas, decision logic, data tables, and original FACEHARD program flow and
text — belong to the estate of Nathan Okun and, per NavWeaps' terms, may not be
used commercially without prior written permission. The Python implementation
and its original additions are Copyright © 2026 Adrian Beale and are released
under the PolyForm Noncommercial License 1.0.0. The combined distribution is
for noncommercial use only; commercial use may require permission from both
rights holders. See LICENSE and NOTICE.
Release files for facehard 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| facehard-1.0.0.tar.gz | 101.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| facehard-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 202.7 kB
Release files / facehard-1.0.0.tar.gz
| Download URL | facehard-1.0.0.tar.gz |
|---|---|
| Size | 101.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e8a14e22e7da0175d60548a7d048efbe4f31754545e3ba7eb6600468f9382c7e
|
|
BLAKE2b-256 checksum How to use checksums |
4f05e7a26843b71244ce1715aa5b6b91c2291c2b21b321778edca72eee19a7d6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.0
|
Release files / facehard-1.0.0-py3-none-any.whl
| Download URL | facehard-1.0.0-py3-none-any.whl |
|---|---|
| Size | 101.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4a76c062a27dd2f3f1a58af58021cb38f040203c51ca1377bbb583fccdd040ed
|
|
BLAKE2b-256 checksum How to use checksums |
152fcf23bbc658bf26810f9bb6e0ff7e79f138b4f87f603b68f58e63a770ff9b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.0
|