Metadata-Version: 2.4
Name: facehard
Version: 1.0.0
Summary: Native Python port of Nathan Okun's FACEHARD 8.0 face-hardened naval armour penetration model, with a CLI.
Author: Adrian Beale
License: PolyForm-Noncommercial-1.0.0
Project-URL: Homepage, https://github.com/TinBane/facehard
Project-URL: Repository, https://github.com/TinBane/facehard
Keywords: naval,armour,penetration,ballistics,facehard,okun
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Science/Research
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Topic :: Games/Entertainment :: Simulation
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Dynamic: license-file

# 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](http://www.navweaps.com/index_nathan/index_nathan.php).

## Install

```bash
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

```python
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:

```python
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](http://www.navweaps.com/index_nathan/index_nathan.php)' 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`](LICENSE) and [`NOTICE`](NOTICE).
