Prepare for dealing out saved seeds
This commit is contained in:
parent
00ed414b1a
commit
05294eaa4b
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
@ -40,11 +40,11 @@ function board:init(ruleset)
|
|||||||
return new.rank==lst.rank-1
|
return new.rank==lst.rank-1
|
||||||
end))
|
end))
|
||||||
|
|
||||||
self:deal()
|
self:deal(12889>>16)
|
||||||
end
|
end
|
||||||
|
|
||||||
function board:deal()
|
function board:deal(seed)
|
||||||
local deal=deal(self.ruleset)
|
local deal=deal(self.ruleset,seed)
|
||||||
local n_usable_slots=self.ruleset.n_slots - 1
|
local n_usable_slots=self.ruleset.n_slots - 1
|
||||||
|
|
||||||
for i=1,#self.ruleset.deck.aces do
|
for i=1,#self.ruleset.deck.aces do
|
||||||
|
168
dealer.lua
168
dealer.lua
@ -1,137 +1,45 @@
|
|||||||
function deal(ruleset)
|
function deal(ruleset,seed)
|
||||||
local n_usable_cards=ruleset.n_usable_cards
|
local n_usable_cards=ruleset.n_usable_cards
|
||||||
local n_final_slots=ruleset.n_slots-1
|
local n_usable_slots=ruleset.n_slots-1
|
||||||
local n_usable_slots=n_final_slots+1
|
local tower_height = n_usable_cards\n_usable_slots
|
||||||
local tower_height = n_usable_cards\n_final_slots
|
|
||||||
|
|
||||||
-- prototype
|
local r=rnd()
|
||||||
local deal1,generate_wells,generate_pops,accepts
|
|
||||||
local deal1=function()
|
|
||||||
local slots,max_height={},{}
|
|
||||||
for i=0,n_final_slots do
|
|
||||||
slots[i]={}
|
|
||||||
max_height[i]=tower_height
|
|
||||||
end
|
|
||||||
max_height[0]=1
|
|
||||||
|
|
||||||
local wells=generate_wells()
|
--
|
||||||
local pops=generate_pops(wells)
|
srand(seed)
|
||||||
|
local slots={}
|
||||||
local function pek(lst)
|
for i=1,n_usable_slots do
|
||||||
return lst[#lst]
|
slots[i]={}
|
||||||
end
|
|
||||||
|
|
||||||
local function pop(lst)
|
|
||||||
return deli(lst,#lst)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function pop_accepted_card(lst)
|
|
||||||
local card=lst[#lst]
|
|
||||||
if (card and accepts(lst[#lst-1],card)) return pop(lst)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function find_home(card,allow_too_tall)
|
|
||||||
assert(card!=0)
|
|
||||||
local acceptors={}
|
|
||||||
|
|
||||||
for s=0,#slots do
|
|
||||||
local n=#slots[s]
|
|
||||||
if ((allow_too_tall and accepts(pek(slots[s]),card)) or n<max_height[s]) add(acceptors,s)
|
|
||||||
end
|
|
||||||
|
|
||||||
local a=rnd(acceptors)
|
|
||||||
assert(a)
|
|
||||||
add(slots[a],card)
|
|
||||||
end
|
|
||||||
|
|
||||||
local original_pops=#pops
|
|
||||||
while #pops>0 do
|
|
||||||
local w=pop(pops)
|
|
||||||
local card=pop(wells[w])
|
|
||||||
find_home(card,true)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- fix any stacks that are too tall
|
|
||||||
max_height[0]=0 -- auxiliary slot must be empty
|
|
||||||
for i=0,#slots do
|
|
||||||
while #slots[i]>max_height[i] do
|
|
||||||
local card=pop(slots[i])
|
|
||||||
find_home(card,false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- get rid of auxiliary slot
|
|
||||||
slots[0]=nil
|
|
||||||
local shuffles=1
|
|
||||||
local actual_shuffles=1
|
|
||||||
for s=1,#slots do
|
|
||||||
local extra_shuf=shuffles+1
|
|
||||||
while (ruleset.deck.instantly_accepted[pek(slots[s])]) do
|
|
||||||
shuf(slots[s])
|
|
||||||
shuffles=extra_shuf
|
|
||||||
actual_shuffles+=1
|
|
||||||
if (actual_shuffles>=5) return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for i=shuffles,2 do
|
|
||||||
shuf(rnd(slots))
|
|
||||||
end
|
|
||||||
for s=1,#slots do
|
|
||||||
assert(#slots[s]==tower_height)
|
|
||||||
end
|
|
||||||
|
|
||||||
return slots
|
|
||||||
end
|
|
||||||
generate_wells=function()
|
|
||||||
local split_point=flr(rnd()*(ruleset.n_arcana+1))
|
|
||||||
local wells={}
|
|
||||||
for i=1,ruleset.n_suits+2 do
|
|
||||||
wells[i]={}
|
|
||||||
end
|
|
||||||
for r=2,ruleset.n_cards_per_suit do
|
|
||||||
for s=1,ruleset.n_suits do
|
|
||||||
local card=ruleset.n_cards_per_suit * (s - 1) + (r - 1) + 1
|
|
||||||
assert(card!=0)
|
|
||||||
add(wells[s],card)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local first_arcana=ruleset.n_suits*ruleset.n_cards_per_suit+1
|
|
||||||
local arcana0=ruleset.n_suits
|
|
||||||
local arcana1=arcana0+1
|
|
||||||
for r=0,split_point-1 do
|
|
||||||
assert(first_arcana+r!=0)
|
|
||||||
add(wells[arcana0],first_arcana+r)
|
|
||||||
end
|
|
||||||
for r=ruleset.n_arcana-1,split_point,-1 do
|
|
||||||
assert(first_arcana+r!=0)
|
|
||||||
add(wells[arcana1],first_arcana+r)
|
|
||||||
end
|
|
||||||
return wells
|
|
||||||
end
|
|
||||||
generate_pops=function(wells)
|
|
||||||
local pops={}
|
|
||||||
for w=1,#wells do
|
|
||||||
for _=1,#wells[w] do
|
|
||||||
add(pops,w)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
shuf(pops)
|
|
||||||
return pops
|
|
||||||
end
|
|
||||||
accepts=function(c0,c1)
|
|
||||||
assert(c0!=0)
|
|
||||||
assert(c1!=0)
|
|
||||||
assert(c1!=nil)
|
|
||||||
if (c0==nil) return true
|
|
||||||
c0=ruleset.deck.cards[c0]
|
|
||||||
c1=ruleset.deck.cards[c1]
|
|
||||||
return c0.suit==c1.suit and (c1.rank == c0.rank+1 or c0.rank==c1.rank+1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
local cards={}
|
||||||
local d=deal1()
|
for i=1,#ruleset.deck.cards do
|
||||||
if (d) return d
|
add(cards,i)
|
||||||
print("retrying")
|
|
||||||
end
|
end
|
||||||
|
for a in all(ruleset.deck.aces) do
|
||||||
|
del(cards,a)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(#cards==n_usable_cards)
|
||||||
|
|
||||||
|
local slot=0
|
||||||
|
while #cards>0 do
|
||||||
|
local arr=slots[1+(slot%#slots)]
|
||||||
|
local i=flr(rnd(#cards))
|
||||||
|
local card=cards[i+1]
|
||||||
|
if (ruleset.deck.instantly_accepted[card] and #arr==0) then
|
||||||
|
-- do not use
|
||||||
|
else
|
||||||
|
add(arr,card,1)
|
||||||
|
deli(cards,i+1)
|
||||||
|
slot+=1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1,#slots do
|
||||||
|
assert(#slots[i]==tower_height)
|
||||||
|
end
|
||||||
|
|
||||||
|
srand(r)
|
||||||
|
return slots
|
||||||
end
|
end
|
1
main.p8
1
main.p8
@ -10,6 +10,7 @@ __lua__
|
|||||||
#include layout.lua
|
#include layout.lua
|
||||||
#include ruleset.lua
|
#include ruleset.lua
|
||||||
#include progression.lua
|
#include progression.lua
|
||||||
|
#include seed_constants.lua
|
||||||
#include main.lua
|
#include main.lua
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
|
156
seed_compressor/compressor.py
Normal file
156
seed_compressor/compressor.py
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
from parse_cart import Pico8Cart
|
||||||
|
import struct
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ff = load_seeds("input/fortunes_foundation.txt")
|
||||||
|
|
||||||
|
ffdata = delta_4b(ff)
|
||||||
|
offset_ffdata = 0
|
||||||
|
|
||||||
|
augment_map("../main.p8", "../seed_constants.lua", ffdata, {
|
||||||
|
"ffdata": 0
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def augment_map(target, target2, constants_file, binary, offsets):
|
||||||
|
assert isinstance(binary, bytes) and len(binary) < 8192 # length of mapdata
|
||||||
|
print(f"Length of basic extra map data: {len(binary)}")
|
||||||
|
mapdata = (binary + bytes([0] * 8192))[:8192]
|
||||||
|
|
||||||
|
cart = Pico8Cart.load(target)
|
||||||
|
def touch_map(memory):
|
||||||
|
memory[0x0:0x1000] = mapdata[0x1000:0x2000]
|
||||||
|
def touch_gfx(memory):
|
||||||
|
memory[0x1000:0x2000] = mapdata[0x0000:0x1000]
|
||||||
|
|
||||||
|
cart.touch("__map__", touch_map)
|
||||||
|
cart.touch("__gfx__", touch_gfx)
|
||||||
|
cart.save(target)
|
||||||
|
|
||||||
|
with open(constants_file, "wt") as f:
|
||||||
|
f.write("seed_constants={\n")
|
||||||
|
for i, (k, v) in enumerate(offsets.items()):
|
||||||
|
sep = "," if i < len(offsets) - 1 else ""
|
||||||
|
f.write(f" {k}={v+0x1000}{sep}\n")
|
||||||
|
f.write("}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def load_seeds(fname):
|
||||||
|
seeds = set()
|
||||||
|
with open(fname, "rt") as f:
|
||||||
|
for line in f:
|
||||||
|
seeds.add(int(line))
|
||||||
|
|
||||||
|
return list(sorted(seeds))
|
||||||
|
|
||||||
|
|
||||||
|
def analyze(seeds):
|
||||||
|
def peek_at_seeds(seeds):
|
||||||
|
print("Seeds modulo various")
|
||||||
|
for i in range(0, 30):
|
||||||
|
matches = [s for s in seeds if s%30 == i]
|
||||||
|
print("- {}: {} (max {})".format(i, len(matches), max(matches)))
|
||||||
|
print()
|
||||||
|
|
||||||
|
peek_at_seeds(seeds)
|
||||||
|
# seeds=seeds[:8192] # stick to the range with a realistic distribution
|
||||||
|
|
||||||
|
print("{} seeds".format(len(seeds)))
|
||||||
|
for encoding in [
|
||||||
|
naive, delta_8b, delta_3b, delta_4b, delta_5b, zlib_delta_4b, zlib_delta_8b
|
||||||
|
]:
|
||||||
|
print("{} encoding: {} bytes".format(encoding.__name__, len(encoding(seeds))))
|
||||||
|
|
||||||
|
def naive(seeds):
|
||||||
|
return b"".join(struct.pack("<I", s) for s in seeds)
|
||||||
|
|
||||||
|
def delta_8b(seeds):
|
||||||
|
out = b""
|
||||||
|
acc = 0
|
||||||
|
i = 0
|
||||||
|
while i < len(seeds):
|
||||||
|
diff = seeds[i] - acc
|
||||||
|
if diff > 255:
|
||||||
|
out += struct.pack("<B", 0)
|
||||||
|
acc += 255
|
||||||
|
else:
|
||||||
|
out += struct.pack("<B", diff)
|
||||||
|
acc = seeds[i]
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
def zlib_delta_8b(seeds):
|
||||||
|
return zlib.compress(delta_8b(seeds))
|
||||||
|
|
||||||
|
|
||||||
|
def delta_4b(seeds):
|
||||||
|
out_nibbles = []
|
||||||
|
acc = 0
|
||||||
|
i = 0
|
||||||
|
while i < len(seeds):
|
||||||
|
diff = seeds[i] - acc
|
||||||
|
if diff > 15:
|
||||||
|
out_nibbles.append(0)
|
||||||
|
acc += 15
|
||||||
|
else:
|
||||||
|
out_nibbles.append(diff)
|
||||||
|
acc = seeds[i]
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
while len(out_nibbles) % 2 != 0:
|
||||||
|
out_nibbles.append(0)
|
||||||
|
|
||||||
|
out = b""
|
||||||
|
for i in range(0, len(out_nibbles), 2):
|
||||||
|
out += bytes([(out_nibbles[i] << 4) + out_nibbles[i+1]])
|
||||||
|
return out
|
||||||
|
|
||||||
|
def zlib_delta_4b(seeds):
|
||||||
|
return zlib.compress(delta_4b(seeds))
|
||||||
|
|
||||||
|
def delta_3b(seeds):
|
||||||
|
return delta_nb(seeds, 3)
|
||||||
|
|
||||||
|
def delta_5b(seeds):
|
||||||
|
return delta_nb(seeds, 5)
|
||||||
|
|
||||||
|
def delta_nb(seeds, n):
|
||||||
|
out_fibbles = []
|
||||||
|
acc = 0
|
||||||
|
i = 0
|
||||||
|
while i < len(seeds):
|
||||||
|
diff = seeds[i] - acc
|
||||||
|
if diff > (1<<n)-1:
|
||||||
|
out_fibbles.append(0)
|
||||||
|
acc += (1<<n)-1
|
||||||
|
else:
|
||||||
|
out_fibbles.append(diff)
|
||||||
|
acc = seeds[i]
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
while len(out_fibbles) % 8 != 0:
|
||||||
|
out_fibbles.append(0)
|
||||||
|
|
||||||
|
out = b""
|
||||||
|
for i in range(0, len(out_fibbles), 8):
|
||||||
|
chunk = out_fibbles[i:i+8]
|
||||||
|
chunk_bits = sum([
|
||||||
|
[i&(1<<x)==0 for x in reversed(range(n))]
|
||||||
|
for i in chunk
|
||||||
|
], [])
|
||||||
|
assert len(chunk_bits)==n*8
|
||||||
|
chunk_bytes = [
|
||||||
|
sum([v*1<<(7-i) for i, v in enumerate(byte)])
|
||||||
|
for byte in [
|
||||||
|
chunk_bits[o:o+8]
|
||||||
|
for o in range(0, n*8, 8)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
out += bytes(chunk_bytes)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
10316
seed_compressor/input/fortunes_foundation.txt
Normal file
10316
seed_compressor/input/fortunes_foundation.txt
Normal file
File diff suppressed because it is too large
Load Diff
103
seed_compressor/parse_cart.py
Normal file
103
seed_compressor/parse_cart.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
SECTIONS = ("PREHEADER", "__lua__", "__gfx__", "__gff__", "__label__", "__map__", "__sfx__", "__music__")
|
||||||
|
|
||||||
|
class Pico8Cart(object):
|
||||||
|
def __init__(self, sections):
|
||||||
|
self._sections = sections
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, fname):
|
||||||
|
sections = {}
|
||||||
|
|
||||||
|
with open(fname, "rt") as f:
|
||||||
|
section_name = "PREHEADER"
|
||||||
|
section_text = []
|
||||||
|
|
||||||
|
def add_section():
|
||||||
|
sections[section_name] = "\n".join(section_text)
|
||||||
|
section_text.clear()
|
||||||
|
|
||||||
|
for line in f:
|
||||||
|
line = line.rstrip("\r\n")
|
||||||
|
if line in SECTIONS:
|
||||||
|
add_section()
|
||||||
|
|
||||||
|
section_name = line
|
||||||
|
else:
|
||||||
|
section_text.append(line)
|
||||||
|
|
||||||
|
add_section()
|
||||||
|
|
||||||
|
return Pico8Cart(sections)
|
||||||
|
|
||||||
|
def touch(self, section, cb):
|
||||||
|
self._sections[section] = touch(section, self._sections.get(section), cb)
|
||||||
|
|
||||||
|
def save(self, fname):
|
||||||
|
with open(fname, "wt") as f:
|
||||||
|
for s in SECTIONS:
|
||||||
|
val = self._sections.get(s)
|
||||||
|
val = canonize(s, val)
|
||||||
|
|
||||||
|
if val:
|
||||||
|
if s != "PREHEADER":
|
||||||
|
f.write(f"{s}\n")
|
||||||
|
f.write(f"{val}\n")
|
||||||
|
|
||||||
|
def canonize(section, val):
|
||||||
|
return touch(section, val, lambda _: ())
|
||||||
|
|
||||||
|
def touch(section, val, cb):
|
||||||
|
if section in ("__gfx__", "__map__"):
|
||||||
|
if val is None:
|
||||||
|
val = ""
|
||||||
|
length = 0x2000 if section == "__gfx__" else 0x1000
|
||||||
|
row_width=128 if section=="__gfx__" else 256
|
||||||
|
msb_first = section == "__map__"
|
||||||
|
memory = from_binary(val, length, msb_first)
|
||||||
|
cb(memory)
|
||||||
|
return to_binary(memory, length, msb_first, row_width)
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
def from_binary(pico_data: str, length: int, msb_first: bool):
|
||||||
|
hex_data = "".join(pico_data.split("\n"))
|
||||||
|
byte_values = []
|
||||||
|
for i in range(0,len(hex_data),2):
|
||||||
|
x0 = int(hex_data[i],16)
|
||||||
|
x1 = int(hex_data[i+1],16)
|
||||||
|
byte_values.append(
|
||||||
|
(x0 << 4) + x1
|
||||||
|
if msb_first else
|
||||||
|
(x1 << 4) + x0
|
||||||
|
)
|
||||||
|
|
||||||
|
assert(len(byte_values) <= length)
|
||||||
|
byte_values = (byte_values + [0] * length)[:length]
|
||||||
|
return bytearray(byte_values)
|
||||||
|
|
||||||
|
def to_binary(memory: bytearray, length: int, msb_first: bool, row_width: int):
|
||||||
|
assert(len(memory) == length)
|
||||||
|
|
||||||
|
HEX_CHARS = "0123456789abcdef"
|
||||||
|
|
||||||
|
chars = []
|
||||||
|
for i in range(length):
|
||||||
|
byte = memory[i]
|
||||||
|
msb = (byte & 0xf0) >> 4
|
||||||
|
lsb = byte & 0x0f
|
||||||
|
|
||||||
|
if msb_first:
|
||||||
|
chars.append(HEX_CHARS[msb])
|
||||||
|
chars.append(HEX_CHARS[lsb])
|
||||||
|
else:
|
||||||
|
chars.append(HEX_CHARS[lsb])
|
||||||
|
chars.append(HEX_CHARS[msb])
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
for i in range(0, len(chars), row_width):
|
||||||
|
lines.append("".join(chars[i:i+row_width]))
|
||||||
|
zeroes = "0" * row_width
|
||||||
|
|
||||||
|
while lines and lines[-1] == zeroes:
|
||||||
|
lines.pop()
|
||||||
|
return "\n".join(lines)
|
3
seed_constants.lua
Normal file
3
seed_constants.lua
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
seed_constants={
|
||||||
|
ffdata=4096
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{ruleset::{Card, CardMetadata, Setup}, smart_dealer::Deal, zobrist::{Feature, Zobrist}};
|
use crate::{ruleset::{Card, CardMetadata, Setup}, dumb_dealer::Deal, zobrist::{Feature, Zobrist}};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash)]
|
#[derive(Clone, Copy, Debug, Hash)]
|
||||||
pub enum Move {
|
pub enum Move {
|
||||||
|
44
simulator/src/dumb_dealer.rs
Normal file
44
simulator/src/dumb_dealer.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use crate::{pico_rng::PicoRng, ruleset::{Card, Setup}};
|
||||||
|
|
||||||
|
pub struct Deal {
|
||||||
|
pub slots: Vec<Vec<Card>>,
|
||||||
|
}
|
||||||
|
impl Deal {
|
||||||
|
pub fn deal(setup: &Setup, seed: u32) -> Deal {
|
||||||
|
let n_usable_cards = setup.ruleset.usable_n_cards();
|
||||||
|
let n_usable_slots = setup.ruleset.n_slots - 1;
|
||||||
|
let tower_height = n_usable_cards / n_usable_slots;
|
||||||
|
|
||||||
|
let mut rng = PicoRng::srand(seed);
|
||||||
|
|
||||||
|
let mut slots: Vec<Vec<Card>> = vec![vec![]; n_usable_slots as usize];
|
||||||
|
let mut cards= vec![];
|
||||||
|
for i in 0..setup.deck.cards.len() {
|
||||||
|
let card = Card(i as u8);
|
||||||
|
if !setup.deck.aces.contains(&card) {
|
||||||
|
cards.push(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut slot= 0;
|
||||||
|
while cards.len() > 0 {
|
||||||
|
let n = slots.len();
|
||||||
|
let arr = &mut slots[slot % n];
|
||||||
|
let i = (rng.rnd((cards.len() * 0x10000) as u32) / 0x10000) as usize;
|
||||||
|
let card = cards[i];
|
||||||
|
if setup.deck.instantly_accepted.contains(&card) && arr.len() == 0 {
|
||||||
|
// do not use
|
||||||
|
} else {
|
||||||
|
arr.insert(0, card);
|
||||||
|
cards.remove(i);
|
||||||
|
slot += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..slots.len() {
|
||||||
|
assert_eq!(tower_height as usize, slots[i].len());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Deal { slots };
|
||||||
|
}
|
||||||
|
}
|
@ -1,39 +1,42 @@
|
|||||||
|
use std::{borrow::Borrow, fs::File, io::Write, sync::{Arc, Mutex}};
|
||||||
|
|
||||||
use board::Board;
|
use board::Board;
|
||||||
use pico_rng::PicoRng;
|
|
||||||
use ruleset::Ruleset;
|
use ruleset::Ruleset;
|
||||||
use seen::Seen;
|
use seen::Seen;
|
||||||
|
|
||||||
use crate::smart_dealer::Deal;
|
use crate::dumb_dealer::Deal;
|
||||||
|
|
||||||
mod board;
|
mod board;
|
||||||
mod ruleset;
|
mod ruleset;
|
||||||
mod pico_rng;
|
mod pico_rng;
|
||||||
mod seen;
|
mod seen;
|
||||||
mod smart_dealer;
|
mod dumb_dealer;
|
||||||
mod zobrist;
|
mod zobrist;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
/*
|
||||||
let mut rng = PicoRng::srand(0x20000);
|
let mut rng = PicoRng::srand(0x20000);
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
println!("{}", rng.rnd(0x10000));
|
println!("{}", rng.rnd(0x10000));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
let ruleset = Ruleset {
|
let ruleset = Ruleset {
|
||||||
n_slots: 11,
|
n_slots: 11,
|
||||||
n_suits: 5,
|
n_suits: 5,
|
||||||
n_cards_per_suit: 10,
|
n_cards_per_suit: 10,
|
||||||
n_arcana: 25
|
n_arcana: 25
|
||||||
};
|
};
|
||||||
/*
|
*/
|
||||||
let ruleset = Ruleset {
|
let ruleset = Ruleset {
|
||||||
n_slots: 11,
|
n_slots: 11,
|
||||||
n_suits: 4,
|
n_suits: 4,
|
||||||
n_cards_per_suit: 13,
|
n_cards_per_suit: 13,
|
||||||
n_arcana: 22
|
n_arcana: 22
|
||||||
};
|
};
|
||||||
*/
|
|
||||||
/*
|
/*
|
||||||
let ruleset = Ruleset {
|
let ruleset = Ruleset {
|
||||||
n_slots: 5,
|
n_slots: 5,
|
||||||
@ -65,15 +68,45 @@ fn main() {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const THREADS: usize = 28;
|
||||||
|
let winnable_seeds_file = Arc::new(Mutex::new(File::create("winnable_seeds_multithreaded.txt").expect("should be able to create")));
|
||||||
|
|
||||||
|
let mut threads = Vec::new();
|
||||||
|
for i in 0..THREADS {
|
||||||
|
let setup2 = setup.clone();
|
||||||
|
let wsf = winnable_seeds_file.clone();
|
||||||
|
threads.push(std::thread::spawn(move || {
|
||||||
|
winnable_seeds_thread(
|
||||||
|
&setup2,
|
||||||
|
|seed| {
|
||||||
|
let mut f = wsf.lock().expect("must be able to lock");
|
||||||
|
write!(f, "{}\n", seed).expect("write should succeed");
|
||||||
|
f.flush().expect("flush should succeed");
|
||||||
|
},
|
||||||
|
i as u32,
|
||||||
|
THREADS as u32
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in threads {
|
||||||
|
let _ = i.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn winnable_seeds_thread(setup: &ruleset::Setup, mut cb: impl FnMut(u32), start: u32, step: u32) {
|
||||||
let mut winnable = 0;
|
let mut winnable = 0;
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
loop {
|
|
||||||
|
for seed in (start..0xffffffff).step_by(step as usize) {
|
||||||
let mut board = Board::new(&setup);
|
let mut board = Board::new(&setup);
|
||||||
board.deal(Deal::deal(&setup, &mut rand::thread_rng()));
|
board.deal(Deal::deal(&setup, seed));
|
||||||
board.display();
|
board.display();
|
||||||
|
|
||||||
if is_winnable(board) {
|
if is_winnable(board) {
|
||||||
winnable += 1;
|
winnable += 1;
|
||||||
|
cb(seed);
|
||||||
}
|
}
|
||||||
total += 1;
|
total += 1;
|
||||||
println!("winnable: {}/{} ({}%)", winnable, total, (100.0 * winnable as f32)/(total as f32));
|
println!("winnable: {}/{} ({}%)", winnable, total, (100.0 * winnable as f32)/(total as f32));
|
||||||
@ -84,13 +117,19 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_winnable(mut board: Board<'_>) -> bool {
|
fn is_winnable(mut board: Board<'_>) -> bool {
|
||||||
let mut seen = Seen::new();
|
return
|
||||||
|
explore(20, &mut board) ||
|
||||||
explore(0, &mut board, &mut seen)
|
explore(200, &mut board)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn explore(depth: usize, board: &mut Board<'_>, seen: &mut Seen) -> bool {
|
fn explore(max_depth: usize, board: &mut Board<'_>) -> bool {
|
||||||
if depth > 200 {
|
let mut seen = Seen::new();
|
||||||
|
explore2(max_depth, 0, board, &mut seen)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn explore2(max_depth: usize, depth: usize, board: &mut Board<'_>, seen: &mut Seen) -> bool {
|
||||||
|
if depth > max_depth {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +147,7 @@ fn explore(depth: usize, board: &mut Board<'_>, seen: &mut Seen) -> bool {
|
|||||||
let hash_1 = board.zobrist_key();
|
let hash_1 = board.zobrist_key();
|
||||||
board.perform(m);
|
board.perform(m);
|
||||||
// println!("try: {:X?} {:?}", board.zobrist_key(), m);
|
// println!("try: {:X?} {:?}", board.zobrist_key(), m);
|
||||||
if explore(depth + 1, board, seen) {
|
if explore2(max_depth, depth + 1, board, seen) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// println!("undo: {:X?} {:?}", board.zobrist_key(), m);
|
// println!("undo: {:X?} {:?}", board.zobrist_key(), m);
|
||||||
|
@ -19,7 +19,7 @@ impl PicoRng {
|
|||||||
|
|
||||||
pub fn rnd(&mut self, n: u32) -> u32 {
|
pub fn rnd(&mut self, n: u32) -> u32 {
|
||||||
self.hi = self.hi.rotate_left(0x10).wrapping_add(self.lo);
|
self.hi = self.hi.rotate_left(0x10).wrapping_add(self.lo);
|
||||||
self.lo += self.hi;
|
self.lo = self.lo.wrapping_add(self.hi);
|
||||||
return self.hi % n
|
return self.hi % n
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Ruleset {
|
pub struct Ruleset {
|
||||||
pub n_slots: u8,
|
pub n_slots: u8,
|
||||||
pub n_suits: u8,
|
pub n_suits: u8,
|
||||||
@ -7,6 +8,7 @@ pub struct Ruleset {
|
|||||||
pub n_arcana: u8,
|
pub n_arcana: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Deck {
|
pub struct Deck {
|
||||||
pub aces: Vec<Card>,
|
pub aces: Vec<Card>,
|
||||||
pub suits: Vec<u8>,
|
pub suits: Vec<u8>,
|
||||||
@ -14,6 +16,7 @@ pub struct Deck {
|
|||||||
pub instantly_accepted: Vec<Card>,
|
pub instantly_accepted: Vec<Card>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Setup {
|
pub struct Setup {
|
||||||
pub ruleset: Ruleset,
|
pub ruleset: Ruleset,
|
||||||
pub deck: Deck
|
pub deck: Deck
|
||||||
|
10347
simulator/winnable_seeds_multithreaded.txt
Normal file
10347
simulator/winnable_seeds_multithreaded.txt
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user