-
-
Save furrtek/36816e29565e6fd1626392e1f07e5f43 to your computer and use it in GitHub Desktop.
# NGP Horoscope simulator v1.1 | |
# furrtek 2021 | |
# Based on algorithm found in ngp_bios.bin | |
import sys | |
from datetime import datetime | |
if len(sys.argv) == 1: | |
print(sys.argv[0] + " DD/MM/YYYY") | |
exit() | |
dob = datetime.strptime(sys.argv[1], '%d/%m/%Y') | |
current_date = datetime.today() | |
current_day = current_date.day | |
current_month = current_date.month | |
current_year = current_date.year | |
dob_day = dob.day | |
dob_month = dob.month | |
dob_year = dob.year | |
# 0 is Sunday | |
weekday = (datetime(current_year, current_month, current_day).weekday() + 1) % 7 | |
#print("Weekday: %d" % weekday) | |
# January to December | |
zodiac_days = [20, 19, 21, 20, 21, 22, 23, 23, 23, 24, 23, 22] | |
zodiac_signs = [ | |
"Aquarius", | |
"Pisces", | |
"Aries", | |
"Taurus", | |
"Gemini", | |
"Cancer", | |
"Leo", | |
"Virgo", | |
"Libra", | |
"Scorpio", | |
"Sagittarius", | |
"Capricorn"] | |
sign_remap = [11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] | |
lookupA = [1, 7, 6, 5, 4, 3, 2, | |
2, 1, 7, 6, 5, 4, 3, | |
3, 2, 1, 7, 6, 5, 4, | |
4, 3, 2, 1, 7, 6, 5, | |
5, 4, 3, 2, 1, 7, 6, | |
6, 5, 4, 3, 2, 1, 7, | |
7, 6, 5, 4, 3, 2, 1] | |
lookupB = [0, 2, 1, 1, 2, 0, 3, 2, 3, 3, 0, 0, 1, 0, 1, 0] | |
lookupC = [3, 4, 4, 5, 4, 4, 3, 3, 2, 2, 1, 2, 2, 3, 3, 4, 5, 3, 1, 2, 3, 3, 2, 1, 3, 5, 4, 3] | |
e = current_year % 10 # Lowest digit in BCD | |
# ffcc00 | |
dob_month -= 1 | |
zodiac_sign = dob_month | |
if dob_day < zodiac_days[dob_month]: | |
zodiac_sign = dob_month - 1 | |
if zodiac_sign < 0: | |
zodiac_sign = 11 # Warp | |
print("Sign: %s" % zodiac_signs[zodiac_sign]) | |
# ffcde6 | |
zodiac_sign = sign_remap[zodiac_sign] | |
# ffce2c | |
d = current_day % 7 | |
if d == 0: | |
d = 7 | |
if d <= weekday: #ffce4b | |
if dob_month == 0: | |
dob_month = 12 | |
b = 7 - d | |
if dob_month in [4, 6, 9, 11]: | |
a = 30 | |
if dob_month != 2: | |
a = 31 | |
else: | |
if leap_year: #ffce7a - TODO | |
a = 28 | |
else: | |
a = 29 | |
d = (a - b) % 7 #ffce86 | |
b = zodiac_sign | |
if b < 8: | |
b -= 1 | |
else: | |
b -= 8 | |
a = d - 1 | |
xsp6 = a | |
a += b | |
h = a if (a < 7) else (d + b) - 8 | |
wa = lookupA[h * 7 + weekday] | |
h = (wa * wa) & 255 | |
b = (zodiac_sign + e + current_month + h) & 3 | |
var40c2 = b | |
d = (zodiac_sign * 2 + e + current_month + h) & 3 | |
var40c4 = d | |
a = (zodiac_sign * 3 + e + current_month + h) & 3 | |
var40c6 = a | |
if (b == d) and (b == a): | |
if b == 2: | |
var40c4 = 3 | |
var40c6 = 0 | |
elif a == 1: | |
var40c4 = 2 | |
var40c6 = 3 | |
elif a != 0: | |
var40c4 = 0 | |
var40c6 = 1 | |
else: | |
var40c4 = 1 | |
var40c6 = 2 | |
else: | |
# ffcf6a | |
hl = b * 4 | |
if b == d: | |
var40c4 = lookupB[hl + a] | |
else: | |
if b == a: | |
var40c6 = lookupB[hl + d] | |
else: | |
if d == a: | |
var40c2 = lookupB[hl + d * 4] | |
# ffcfb7 | |
d = lookupA[(xsp6 * 7) + weekday] | |
if current_day >= d + 7: | |
b = (current_day - d) // 7 | |
var40c2 += b | |
if var40c2 >= 4: | |
var40c2 -= 4 | |
var40c4 += b | |
if var40c4 >= 4: | |
var40c4 -= 4 | |
var40c6 += b | |
if var40c6 >= 4: | |
var40c6 -= 4 | |
# ffd049 | |
zodiac_sign += weekday | |
level_money = lookupC[var40c2 * 7 + (zodiac_sign - 1 + b) % 7] | |
level_health = lookupC[var40c4 * 7 + (zodiac_sign + b * 2) % 7] | |
level_romance = lookupC[var40c6 * 7 + (zodiac_sign + 1 + b * 3) % 7] | |
sum = level_money + level_health + level_romance | |
level_general = sum // 3 | |
if sum % 3 == 2: | |
level_general += 1 | |
print("MONEY: %d" % level_money) | |
print("HEALTH: %d" % level_health) | |
print("ROMANCE: %d" % level_romance) | |
print("GENERAL: %d" % level_general) |
This script doesn't match what the NGP does (testing with mame). I'm now taking a stab at reverse engineering the bios algorithm now. I appreciate the script though, it's been a good starting point.
TurfMasta's bot uses an automated emulator actually running the original code, so the results should be correct.
This script is based on the bios disassembly but it was proven buggy several times, indeed.
Let me know if you want the IDA project and my notes.
yeah anything you got I'd love to have. And I didn't know that about turfmasta's bot, that's good to know.
His bot is giving different answers than MAME. But my MAME setup is using the b&w ngp, possibly SNK adjusted the algorithm? I'll see if I can get a ngpc setup going too.
All I was able to find from that time is a partially commented disassembly of the NGPC bios: https://file.io/07BvQGXxmt5n
The horoscope stuff starts around FFCC00.
This is great, thanks! If I succeed in fully figuring it out I'll post a link to the repo here.
The if statement at line 74 feels off to me.
dob_month
was made zero based on line 56, so should the check beif dob_month in [3, 5, 8, 10]:
? I'm also curious why January was wrapped to 12 but no other month got that treatment? Alsoleap_year
is not defined. That seems to be if the person was born in February on a leap year, not if the current year is a leap year.I'm trying to implement the NGP horoscope on the Sensor Watch and my version, this gist and TurfMasta's bluesky bot all give different results :)