facehard 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- facehard-1.0.0/LICENSE +151 -0
- facehard-1.0.0/NOTICE +41 -0
- facehard-1.0.0/PKG-INFO +133 -0
- facehard-1.0.0/README.md +109 -0
- facehard-1.0.0/facehard/__init__.py +51 -0
- facehard-1.0.0/facehard/cli.py +339 -0
- facehard-1.0.0/facehard/emulate.py +1140 -0
- facehard-1.0.0/facehard/enums.py +265 -0
- facehard-1.0.0/facehard/model.py +3759 -0
- facehard-1.0.0/facehard/names.py +165 -0
- facehard-1.0.0/facehard.egg-info/PKG-INFO +133 -0
- facehard-1.0.0/facehard.egg-info/SOURCES.txt +15 -0
- facehard-1.0.0/facehard.egg-info/dependency_links.txt +1 -0
- facehard-1.0.0/facehard.egg-info/entry_points.txt +2 -0
- facehard-1.0.0/facehard.egg-info/top_level.txt +1 -0
- facehard-1.0.0/pyproject.toml +38 -0
- facehard-1.0.0/setup.cfg +4 -0
facehard-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# PolyForm Noncommercial License 1.0.0
|
|
2
|
+
|
|
3
|
+
<https://polyformproject.org/licenses/noncommercial/1.0.0>
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
In order to get any license under these terms, you must agree
|
|
8
|
+
to them as both strict obligations and conditions to all
|
|
9
|
+
your licenses.
|
|
10
|
+
|
|
11
|
+
## Copyright License
|
|
12
|
+
|
|
13
|
+
The licensor grants you a copyright license for the
|
|
14
|
+
software to do everything you might do with the software
|
|
15
|
+
that would otherwise infringe the licensor's copyright
|
|
16
|
+
in it for any permitted purpose. However, you may
|
|
17
|
+
only distribute the software according to [Distribution
|
|
18
|
+
License](#distribution-license) and make changes or new works
|
|
19
|
+
based on the software according to [Changes and New Works
|
|
20
|
+
License](#changes-and-new-works-license).
|
|
21
|
+
|
|
22
|
+
## Distribution License
|
|
23
|
+
|
|
24
|
+
The licensor grants you an additional copyright license
|
|
25
|
+
to distribute copies of the software. Your license
|
|
26
|
+
to distribute covers distributing the software with
|
|
27
|
+
changes and new works permitted by [Changes and New Works
|
|
28
|
+
License](#changes-and-new-works-license).
|
|
29
|
+
|
|
30
|
+
## Notices
|
|
31
|
+
|
|
32
|
+
You must ensure that anyone who gets a copy of any part of
|
|
33
|
+
the software from you also gets a copy of these terms or the
|
|
34
|
+
URL for them above, as well as copies of any plain-text lines
|
|
35
|
+
beginning with `Required Notice:` that the licensor provided
|
|
36
|
+
with the software. For example:
|
|
37
|
+
|
|
38
|
+
> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
|
|
39
|
+
|
|
40
|
+
## Changes and New Works License
|
|
41
|
+
|
|
42
|
+
The licensor grants you an additional copyright license to
|
|
43
|
+
make changes and new works based on the software for any
|
|
44
|
+
permitted purpose.
|
|
45
|
+
|
|
46
|
+
## Patent License
|
|
47
|
+
|
|
48
|
+
The licensor grants you a patent license for the software that
|
|
49
|
+
covers patent claims the licensor can license, or becomes able
|
|
50
|
+
to license, that you would infringe by using the software.
|
|
51
|
+
|
|
52
|
+
## Noncommercial Purposes
|
|
53
|
+
|
|
54
|
+
Any noncommercial purpose is a permitted purpose.
|
|
55
|
+
|
|
56
|
+
## Personal Uses
|
|
57
|
+
|
|
58
|
+
Personal use for research, experiment, and testing for
|
|
59
|
+
the benefit of public knowledge, personal study, private
|
|
60
|
+
entertainment, hobby projects, amateur pursuits, or religious
|
|
61
|
+
observance, without any anticipated commercial application,
|
|
62
|
+
is use for a permitted purpose.
|
|
63
|
+
|
|
64
|
+
## Noncommercial Organizations
|
|
65
|
+
|
|
66
|
+
Use by any charitable organization, educational institution,
|
|
67
|
+
public research organization, public safety or health
|
|
68
|
+
organization, environmental protection organization,
|
|
69
|
+
or government institution is use for a permitted purpose
|
|
70
|
+
regardless of the source of funding or obligations resulting
|
|
71
|
+
from the funding.
|
|
72
|
+
|
|
73
|
+
## Fair Use
|
|
74
|
+
|
|
75
|
+
You may have "fair use" rights for the software under the
|
|
76
|
+
law. These terms do not limit them.
|
|
77
|
+
|
|
78
|
+
## No Other Rights
|
|
79
|
+
|
|
80
|
+
These terms do not allow you to sublicense or transfer any of
|
|
81
|
+
your licenses to anyone else, or prevent the licensor from
|
|
82
|
+
granting licenses to anyone else. These terms do not imply
|
|
83
|
+
any other licenses.
|
|
84
|
+
|
|
85
|
+
## Patent Defense
|
|
86
|
+
|
|
87
|
+
If you make any written claim that the software infringes or
|
|
88
|
+
contributes to infringement of any patent, your patent license
|
|
89
|
+
for the software granted under these terms ends immediately. If
|
|
90
|
+
your company makes such a claim, your patent license ends
|
|
91
|
+
immediately for work on behalf of your company.
|
|
92
|
+
|
|
93
|
+
## Violations
|
|
94
|
+
|
|
95
|
+
The first time you are notified in writing that you have
|
|
96
|
+
violated any of these terms, or done anything with the software
|
|
97
|
+
not covered by your licenses, your licenses can nonetheless
|
|
98
|
+
continue if you come into full compliance with these terms,
|
|
99
|
+
and take practical steps to correct past violations, within
|
|
100
|
+
32 days of receiving notice. Otherwise, all your licenses
|
|
101
|
+
end immediately.
|
|
102
|
+
|
|
103
|
+
## No Liability
|
|
104
|
+
|
|
105
|
+
***As far as the law allows, the software comes as is, without
|
|
106
|
+
any warranty or condition, and the licensor will not be liable
|
|
107
|
+
to you for any damages arising out of these terms or the use
|
|
108
|
+
or nature of the software, under any kind of legal claim.***
|
|
109
|
+
|
|
110
|
+
## Definitions
|
|
111
|
+
|
|
112
|
+
The **licensor** is the individual or entity offering these
|
|
113
|
+
terms, and the **software** is the software the licensor makes
|
|
114
|
+
available under these terms.
|
|
115
|
+
|
|
116
|
+
**You** refers to the individual or entity agreeing to these
|
|
117
|
+
terms.
|
|
118
|
+
|
|
119
|
+
**Your company** is any legal entity, sole proprietorship,
|
|
120
|
+
or other kind of organization that you work for, plus all
|
|
121
|
+
organizations that have control over, are under the control of,
|
|
122
|
+
or are under common control with that organization. **Control**
|
|
123
|
+
means ownership of substantially all the assets of an entity,
|
|
124
|
+
or the power to direct its management and policies by vote,
|
|
125
|
+
contract, or otherwise. Control can be direct or indirect.
|
|
126
|
+
|
|
127
|
+
**Your licenses** are all the licenses granted to you for the
|
|
128
|
+
software under these terms.
|
|
129
|
+
|
|
130
|
+
**Use** means anything you do with the software requiring one
|
|
131
|
+
of your licenses.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
Required Notice: Copyright (c) the estate of Nathan Okun. The underlying
|
|
136
|
+
penetration mechanics and associated intellectual property — including the
|
|
137
|
+
empirical formulas, algorithms, decision logic, historical data tables, and
|
|
138
|
+
the original FACEHARD program flow, interaction process, and reproduced
|
|
139
|
+
results/definition text — belong to the estate of Nathan Okun. No licence to
|
|
140
|
+
that material is granted by Adrian Beale. Its use remains subject to the
|
|
141
|
+
estate's terms, including its non-commercial restriction.
|
|
142
|
+
|
|
143
|
+
Required Notice: Copyright (c) 2026 Adrian Beale. The Python implementation
|
|
144
|
+
and its original additions — including Python-specific code, packaging,
|
|
145
|
+
command-line interface, and additional features — are owned by Adrian Beale
|
|
146
|
+
and licensed under the PolyForm Noncommercial License 1.0.0 above.
|
|
147
|
+
|
|
148
|
+
This combined distribution is offered for non-commercial use only. Commercial
|
|
149
|
+
use may require permission from both the estate of Nathan Okun (for the
|
|
150
|
+
underlying material) and Adrian Beale (for the Python implementation and
|
|
151
|
+
additions). See the NOTICE file for details.
|
facehard-1.0.0/NOTICE
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
FACEHARD (Python port) — attribution and copyright notice
|
|
2
|
+
==========================================================
|
|
3
|
+
|
|
4
|
+
This package is an independent Python port of FACEHARD 8.0, a face-hardened
|
|
5
|
+
naval-armour penetration program written by Nathan Okun in QuickBASIC.
|
|
6
|
+
|
|
7
|
+
Underlying mechanics and original flow — Copyright (c) the estate of Nathan Okun
|
|
8
|
+
-------------------------------------------------------------------------------
|
|
9
|
+
The empirical penetration mechanics and related intellectual property — including
|
|
10
|
+
the formulas, algorithms, ballistic-limit and post-impact decision logic,
|
|
11
|
+
historical projectile/armour data tables, and the original FACEHARD program's
|
|
12
|
+
flow, interaction process, and results/definition text — belong to the estate
|
|
13
|
+
of Nathan Okun. Okun's material is published on NavWeaps under these terms:
|
|
14
|
+
|
|
15
|
+
"All material in this section of NavWeaps is copyrighted by the estate of
|
|
16
|
+
Nathan Okun and may not be used for commercial purposes without prior
|
|
17
|
+
written permission."
|
|
18
|
+
— http://www.navweaps.com/index_nathan/index_nathan.php
|
|
19
|
+
|
|
20
|
+
This port reproduces that material (including Okun's own source-code comments,
|
|
21
|
+
restored from the original FH80MAIN/FH80SBM1/FH80SBM2.BAS modules) for study and
|
|
22
|
+
convenience. It makes NO claim of authorship over the mechanics, original flow,
|
|
23
|
+
data, or text, and does not grant any rights in them beyond what the estate's
|
|
24
|
+
terms allow.
|
|
25
|
+
|
|
26
|
+
Python implementation and additions — Copyright (c) 2026 Adrian Beale
|
|
27
|
+
-----------------------------------------------------------------------
|
|
28
|
+
The Python-specific implementation and original additions — including the
|
|
29
|
+
Python code, packaging, command-line interface, and additional features — are
|
|
30
|
+
owned by Adrian Beale and licensed under the PolyForm Noncommercial License
|
|
31
|
+
1.0.0 (see LICENSE). To keep the whole work consistent with the estate's terms,
|
|
32
|
+
the port as a whole is offered for NONCOMMERCIAL use only.
|
|
33
|
+
|
|
34
|
+
Commercial use of the underlying mechanics or original flow requires prior
|
|
35
|
+
written permission from the estate of Nathan Okun. Commercial use of the
|
|
36
|
+
Python implementation or additions requires permission from Adrian Beale. A
|
|
37
|
+
use of this combined distribution may require permission from both rights
|
|
38
|
+
holders.
|
|
39
|
+
|
|
40
|
+
This is not legal advice. If you intend to redistribute or use this
|
|
41
|
+
commercially, confirm the terms with the rights holders first.
|
facehard-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: facehard
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Native Python port of Nathan Okun's FACEHARD 8.0 face-hardened naval armour penetration model, with a CLI.
|
|
5
|
+
Author: Adrian Beale
|
|
6
|
+
License: PolyForm-Noncommercial-1.0.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/TinBane/facehard
|
|
8
|
+
Project-URL: Repository, https://github.com/TinBane/facehard
|
|
9
|
+
Keywords: naval,armour,penetration,ballistics,facehard,okun
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: Other/Proprietary License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
18
|
+
Classifier: Topic :: Games/Entertainment :: Simulation
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
License-File: NOTICE
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# FACEHARD — native Python port, verbatim clone + CLI
|
|
26
|
+
|
|
27
|
+
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.
|
|
28
|
+
|
|
29
|
+
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.
|
|
30
|
+
|
|
31
|
+
**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.
|
|
32
|
+
|
|
33
|
+
**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).
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pipx install facehard
|
|
39
|
+
facehard --version
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## CLI
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
facehard interactive wizard (modern menus)
|
|
46
|
+
facehard emulate the ORIGINAL FACEHARD interactive session, verbatim
|
|
47
|
+
facehard run [options] one impact; --output narrative|limits|plug|metrics|json|all
|
|
48
|
+
facehard pen [options] penetration thickness at a striking velocity
|
|
49
|
+
facehard list armors|nations|projectiles [--nation N]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`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.
|
|
53
|
+
|
|
54
|
+
## Library API
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from facehard import calc, results, render_results, penetration, names
|
|
58
|
+
|
|
59
|
+
calc(armor=14, nation=1, proj=18, TA=16, D=16, WT=2700, WB=2048, OB=30).vltru
|
|
60
|
+
results(armor=14, nation=1, proj=18, TA=12, D=16, WT=2700, WB=2048, OB=30, VS=2200)
|
|
61
|
+
render_results(armor=14, nation=1, proj=18, TA=12, D=16, WT=2700, WB=2048,
|
|
62
|
+
OB=30, VS=2200, show_limits=True) # verbatim BASIC text
|
|
63
|
+
penetration(armor=14, nation=1, proj=18, D=16, WT=2700, WB=2048, V=2500, OB=0) # 29.5 in
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Every numeric menu choice also has a named `IntEnum` — drop-in for the plain numbers, which stay valid everywhere:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from facehard import penetration, Armor, Nation, USProjectile, projectiles
|
|
70
|
+
|
|
71
|
+
Nation.UK == 2 # original menu numbers, unchanged
|
|
72
|
+
penetration(armor=Armor.BRITISH_CA, nation=Nation.USA,
|
|
73
|
+
proj=USProjectile.APC_6IN_MK35_16IN_MK8, # 16-in Mk 8 (IOWA)
|
|
74
|
+
D=16, WT=2700, WB=2048, V=2500, OB=0)
|
|
75
|
+
|
|
76
|
+
projectiles(Nation.JAPAN).APC_TYPE91 # per-nation projectile menus
|
|
77
|
+
Armor.JAPANESE_VH.menu_text # the original menu line
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`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.
|
|
81
|
+
|
|
82
|
+
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).
|
|
83
|
+
|
|
84
|
+
## Coverage — the whole program
|
|
85
|
+
|
|
86
|
+
| Layer | Status |
|
|
87
|
+
|-------|--------|
|
|
88
|
+
| Ballistic limits (N1–N4, H1–H4) | ported, 100% vs BASIC |
|
|
89
|
+
| Post-impact results (outcome, exit angle, plug weights, remaining velocity, effective BL) | ported, 100% |
|
|
90
|
+
| Verbatim results narrative (`RESULTSPRINT` + damage flags) | ported, word-for-word |
|
|
91
|
+
| Backing (wood/cement/metal) & nose-covering loss (decapping) | ported, 100% |
|
|
92
|
+
| Manual armour-parameter overrides ("modify parameters" screen) | ported, applies + verbatim |
|
|
93
|
+
| Interactive shell (menus, info pages, prompts, re-run state) | ported, verbatim (`facehard emulate`) |
|
|
94
|
+
|
|
95
|
+
Not reproduced: nothing functional — only the DOS graphics-mode screen clears (cosmetic) differ.
|
|
96
|
+
|
|
97
|
+
## Fidelity — how close to the BASIC?
|
|
98
|
+
|
|
99
|
+
`precision_audit.py` (1500 fuzzed examples, deterministic):
|
|
100
|
+
|
|
101
|
+
- **Zero velocity deviations** across 6,818 ballistic-limit and post-impact velocity comparisons.
|
|
102
|
+
- Exit angles agree exactly in the retained 10,000-case differential campaign.
|
|
103
|
+
- The remaining differences are BASIC display rounding of plug weights to one decimal place (maximum 0.05 lb).
|
|
104
|
+
|
|
105
|
+
The generated working oracle uses double precision and contains the documented post-impact-limit correction in `ORACLE_PATCHES.md`.
|
|
106
|
+
|
|
107
|
+
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.
|
|
108
|
+
|
|
109
|
+
## Layout
|
|
110
|
+
|
|
111
|
+
| Path | Role |
|
|
112
|
+
|------|------|
|
|
113
|
+
| `facehard/` | installable package: `model.py`, `emulate.py`, `cli.py`, `names.py` |
|
|
114
|
+
| `fh_oracle` | compiled BASIC oracle; rebuild via `build_qb64.py` + QB64-PE |
|
|
115
|
+
| `facehard.bas` / `build_qb64.py` | the merged BASIC and its generator |
|
|
116
|
+
| `ORACLE_PATCHES.md` | intentional model corrections applied to the generated oracle |
|
|
117
|
+
| `facehard_run.py` | drives `fh_oracle`, parses its output |
|
|
118
|
+
| `facehard_native.py` | back-compat shim → `facehard.model` |
|
|
119
|
+
| `validate_native.py` / `validate_results.py` | limit & post-impact sweeps vs BASIC |
|
|
120
|
+
| `precision_audit.py` | exact deviation audit (identical / off-by-1 / ≥2) |
|
|
121
|
+
| `fuzz_native/narrative/emulate/override.py` | differential fuzzers |
|
|
122
|
+
| `reproduce_table4_native.py` | reproduces the published comparison table, pure Python |
|
|
123
|
+
|
|
124
|
+
## Licence
|
|
125
|
+
|
|
126
|
+
Not open-source. The penetration mechanics and associated IP — including the
|
|
127
|
+
formulas, decision logic, data tables, and original FACEHARD program flow and
|
|
128
|
+
text — belong to the estate of Nathan Okun and, per [NavWeaps](http://www.navweaps.com/index_nathan/index_nathan.php)' terms, may not be
|
|
129
|
+
used commercially without prior written permission. The Python implementation
|
|
130
|
+
and its original additions are Copyright © 2026 Adrian Beale and are released
|
|
131
|
+
under the PolyForm Noncommercial License 1.0.0. The combined distribution is
|
|
132
|
+
for **noncommercial use only**; commercial use may require permission from both
|
|
133
|
+
rights holders. See [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE).
|
facehard-1.0.0/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# FACEHARD — native Python port, verbatim clone + CLI
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
**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.
|
|
8
|
+
|
|
9
|
+
**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).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pipx install facehard
|
|
15
|
+
facehard --version
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## CLI
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
facehard interactive wizard (modern menus)
|
|
22
|
+
facehard emulate the ORIGINAL FACEHARD interactive session, verbatim
|
|
23
|
+
facehard run [options] one impact; --output narrative|limits|plug|metrics|json|all
|
|
24
|
+
facehard pen [options] penetration thickness at a striking velocity
|
|
25
|
+
facehard list armors|nations|projectiles [--nation N]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`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.
|
|
29
|
+
|
|
30
|
+
## Library API
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from facehard import calc, results, render_results, penetration, names
|
|
34
|
+
|
|
35
|
+
calc(armor=14, nation=1, proj=18, TA=16, D=16, WT=2700, WB=2048, OB=30).vltru
|
|
36
|
+
results(armor=14, nation=1, proj=18, TA=12, D=16, WT=2700, WB=2048, OB=30, VS=2200)
|
|
37
|
+
render_results(armor=14, nation=1, proj=18, TA=12, D=16, WT=2700, WB=2048,
|
|
38
|
+
OB=30, VS=2200, show_limits=True) # verbatim BASIC text
|
|
39
|
+
penetration(armor=14, nation=1, proj=18, D=16, WT=2700, WB=2048, V=2500, OB=0) # 29.5 in
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Every numeric menu choice also has a named `IntEnum` — drop-in for the plain numbers, which stay valid everywhere:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from facehard import penetration, Armor, Nation, USProjectile, projectiles
|
|
46
|
+
|
|
47
|
+
Nation.UK == 2 # original menu numbers, unchanged
|
|
48
|
+
penetration(armor=Armor.BRITISH_CA, nation=Nation.USA,
|
|
49
|
+
proj=USProjectile.APC_6IN_MK35_16IN_MK8, # 16-in Mk 8 (IOWA)
|
|
50
|
+
D=16, WT=2700, WB=2048, V=2500, OB=0)
|
|
51
|
+
|
|
52
|
+
projectiles(Nation.JAPAN).APC_TYPE91 # per-nation projectile menus
|
|
53
|
+
Armor.JAPANESE_VH.menu_text # the original menu line
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`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.
|
|
57
|
+
|
|
58
|
+
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).
|
|
59
|
+
|
|
60
|
+
## Coverage — the whole program
|
|
61
|
+
|
|
62
|
+
| Layer | Status |
|
|
63
|
+
|-------|--------|
|
|
64
|
+
| Ballistic limits (N1–N4, H1–H4) | ported, 100% vs BASIC |
|
|
65
|
+
| Post-impact results (outcome, exit angle, plug weights, remaining velocity, effective BL) | ported, 100% |
|
|
66
|
+
| Verbatim results narrative (`RESULTSPRINT` + damage flags) | ported, word-for-word |
|
|
67
|
+
| Backing (wood/cement/metal) & nose-covering loss (decapping) | ported, 100% |
|
|
68
|
+
| Manual armour-parameter overrides ("modify parameters" screen) | ported, applies + verbatim |
|
|
69
|
+
| Interactive shell (menus, info pages, prompts, re-run state) | ported, verbatim (`facehard emulate`) |
|
|
70
|
+
|
|
71
|
+
Not reproduced: nothing functional — only the DOS graphics-mode screen clears (cosmetic) differ.
|
|
72
|
+
|
|
73
|
+
## Fidelity — how close to the BASIC?
|
|
74
|
+
|
|
75
|
+
`precision_audit.py` (1500 fuzzed examples, deterministic):
|
|
76
|
+
|
|
77
|
+
- **Zero velocity deviations** across 6,818 ballistic-limit and post-impact velocity comparisons.
|
|
78
|
+
- Exit angles agree exactly in the retained 10,000-case differential campaign.
|
|
79
|
+
- The remaining differences are BASIC display rounding of plug weights to one decimal place (maximum 0.05 lb).
|
|
80
|
+
|
|
81
|
+
The generated working oracle uses double precision and contains the documented post-impact-limit correction in `ORACLE_PATCHES.md`.
|
|
82
|
+
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
## Layout
|
|
86
|
+
|
|
87
|
+
| Path | Role |
|
|
88
|
+
|------|------|
|
|
89
|
+
| `facehard/` | installable package: `model.py`, `emulate.py`, `cli.py`, `names.py` |
|
|
90
|
+
| `fh_oracle` | compiled BASIC oracle; rebuild via `build_qb64.py` + QB64-PE |
|
|
91
|
+
| `facehard.bas` / `build_qb64.py` | the merged BASIC and its generator |
|
|
92
|
+
| `ORACLE_PATCHES.md` | intentional model corrections applied to the generated oracle |
|
|
93
|
+
| `facehard_run.py` | drives `fh_oracle`, parses its output |
|
|
94
|
+
| `facehard_native.py` | back-compat shim → `facehard.model` |
|
|
95
|
+
| `validate_native.py` / `validate_results.py` | limit & post-impact sweeps vs BASIC |
|
|
96
|
+
| `precision_audit.py` | exact deviation audit (identical / off-by-1 / ≥2) |
|
|
97
|
+
| `fuzz_native/narrative/emulate/override.py` | differential fuzzers |
|
|
98
|
+
| `reproduce_table4_native.py` | reproduces the published comparison table, pure Python |
|
|
99
|
+
|
|
100
|
+
## Licence
|
|
101
|
+
|
|
102
|
+
Not open-source. The penetration mechanics and associated IP — including the
|
|
103
|
+
formulas, decision logic, data tables, and original FACEHARD program flow and
|
|
104
|
+
text — belong to the estate of Nathan Okun and, per [NavWeaps](http://www.navweaps.com/index_nathan/index_nathan.php)' terms, may not be
|
|
105
|
+
used commercially without prior written permission. The Python implementation
|
|
106
|
+
and its original additions are Copyright © 2026 Adrian Beale and are released
|
|
107
|
+
under the PolyForm Noncommercial License 1.0.0. The combined distribution is
|
|
108
|
+
for **noncommercial use only**; commercial use may require permission from both
|
|
109
|
+
rights holders. See [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""FACEHARD — native Python port of Nathan Okun's face-hardened armour
|
|
2
|
+
penetration model (FACEHARD 8.0).
|
|
3
|
+
|
|
4
|
+
Public API:
|
|
5
|
+
calc(...) -> Limits ballistic limits only
|
|
6
|
+
results(...) -> Results limits + post-impact outcome/metrics
|
|
7
|
+
render_results(...) -> str original BASIC results narrative
|
|
8
|
+
penetration(...) -> float plate thickness defeated at a velocity
|
|
9
|
+
armor_info, proj_data parameter tables
|
|
10
|
+
names menu names (ARMORS / NATIONS / PROJECTILES)
|
|
11
|
+
Nation, Armor, BackingMetal, *Projectile named menu numbers (IntEnums);
|
|
12
|
+
projectiles(nation) -> that nation's enum
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from . import names
|
|
16
|
+
from .enums import (
|
|
17
|
+
Armor,
|
|
18
|
+
AustroHungarianProjectile,
|
|
19
|
+
BackingMetal,
|
|
20
|
+
BritishProjectile,
|
|
21
|
+
FrenchProjectile,
|
|
22
|
+
GermanProjectile,
|
|
23
|
+
ItalianProjectile,
|
|
24
|
+
JapaneseProjectile,
|
|
25
|
+
Nation,
|
|
26
|
+
RussianProjectile,
|
|
27
|
+
USProjectile,
|
|
28
|
+
projectiles,
|
|
29
|
+
)
|
|
30
|
+
from .model import (
|
|
31
|
+
Limits,
|
|
32
|
+
Results,
|
|
33
|
+
armor_info,
|
|
34
|
+
calc,
|
|
35
|
+
penetration,
|
|
36
|
+
proj_data,
|
|
37
|
+
render_results,
|
|
38
|
+
results,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
__version__ = "1.0.0"
|
|
42
|
+
#: Version of Nathan Okun's original QuickBASIC FACEHARD this port reproduces.
|
|
43
|
+
MODEL_VERSION = "8.0"
|
|
44
|
+
__all__ = [
|
|
45
|
+
"calc", "results", "render_results", "penetration", "Limits", "Results",
|
|
46
|
+
"armor_info", "proj_data", "names", "__version__", "MODEL_VERSION",
|
|
47
|
+
"Nation", "Armor", "BackingMetal", "projectiles",
|
|
48
|
+
"USProjectile", "BritishProjectile", "GermanProjectile", "FrenchProjectile",
|
|
49
|
+
"ItalianProjectile", "JapaneseProjectile", "AustroHungarianProjectile",
|
|
50
|
+
"RussianProjectile",
|
|
51
|
+
]
|