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; --curved for a strongly curved plate (dome/cylinder/ellipse — enables Okun's curved-plate body-damage rule above 45° obliquity); --soft-cap works|fails answers the original's "do you wish the SOFT AP cap or HOOD to work?" question (only consulted inside the 15–20° shatter zone of mixed results; the header says when it applies); --set NAME=VALUE (repeatable) is the "modify armor plate's parameters" screen (UB, Q, QDAM, CARTWL, CMPND, THNCHL, SOFTSHAT, THKTHN); 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/penetration accept curved=True (strongly curved plate), soft_cap_works=False (the 15–20° soft-cap/hood answer; zomr_applies(...) says whether the original would ask it), and manual armour overrides. results() also carries Okun's trailing rule notes (Results.notes: curved-plate / soft-cap nose-only-shatter / below-NBL notes) that the effective-limit text's "(SEE BELOW)" refers to. calc/results/render_results also 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.1.2
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.1.2.tar.gz | 112.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| facehard-1.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 225.3 kB
Release files / facehard-1.1.2.tar.gz
| Download URL | facehard-1.1.2.tar.gz |
|---|---|
| Size | 112.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a8920812bca0bd943b31bf6f69781978d26413916dd3445a3c980b3c1157eb4e
|
|
BLAKE2b-256 checksum How to use checksums |
2ad3a6c319221e7ee3e7ff60a2674b207da0ec7b97497c5f73ee793a231b4030
|
| 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.1.2-py3-none-any.whl
| Download URL | facehard-1.1.2-py3-none-any.whl |
|---|---|
| Size | 112.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
90ea04417e95b88ed6d925c62ca7453b88f59443eec814728ef870f4caec7975
|
|
BLAKE2b-256 checksum How to use checksums |
9389d6da11e49b9995f00c789704a513c1c762cf0a0ea8d3e8d7b69050af9041
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.0
|