Skip to content

Instantly share code, notes, and snippets.

@furrtek
Last active May 5, 2024 21:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save furrtek/36816e29565e6fd1626392e1f07e5f43 to your computer and use it in GitHub Desktop.
Save furrtek/36816e29565e6fd1626392e1f07e5f43 to your computer and use it in GitHub Desktop.
NGP Horoscope simulator
# 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)
@city41
Copy link

city41 commented May 4, 2024

The if statement at line 74 feels off to me. dob_month was made zero based on line 56, so should the check be if dob_month in [3, 5, 8, 10]:? I'm also curious why January was wrapped to 12 but no other month got that treatment? Also leap_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 :)

@city41
Copy link

city41 commented May 5, 2024

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.

@furrtek
Copy link
Author

furrtek commented May 5, 2024

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.

@city41
Copy link

city41 commented May 5, 2024

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.

@furrtek
Copy link
Author

furrtek commented May 5, 2024

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.

@city41
Copy link

city41 commented May 5, 2024

This is great, thanks! If I succeed in fully figuring it out I'll post a link to the repo here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment