# shop_test.ludic - a counter over fake money and a fake pack: buying and selling, the week's # demand from the shop's own dice, and the three balance invariants a price curve must keep: # a shop is not a fountain, friction falls with standing and never inverts, raw keeps its order import "ludic.shop" import "ludic.base" program ShopTest { numbers float const WOOD: int = 0 const CHANTERELLE: int = 1 const PUFFBALL: int = 2 const TROUT: int = 3 const COOKED: int = 4 const BAIT: int = 5 const ROPE: int = 6 const JACKET: int = 7 const GRUB: int = 8 const BURNT: int = 9 const RAW: float = 0.50 const MADE: float = 1.15 const BOUGHT: float = 0.55 state ShopTestState { rep: int = 0 day: int = 1 money: int = 0 pack: []int = null food_too: bool = false } function fake_rep(shop_test_st: ShopTestState) -> int { return shop_test_st.rep } function fake_day(shop_test_st: ShopTestState) -> int { return shop_test_st.day } function fake_food(shop_test_st: ShopTestState, v: int, it: int) -> bool { return shop_test_st.food_too and v == 1 and (it == TROUT or it == COOKED or it == BURNT) } function fake_money(shop_test_st: ShopTestState) -> int { return shop_test_st.money } function fake_spend(shop_test_st: mut ShopTestState, n: int) -> bool { if n > shop_test_st.money { return false } shop_test_st.money -= n return true } function fake_earn(shop_test_st: mut ShopTestState, n: int) -> void { shop_test_st.money += n } function fake_count(shop_test_st: ShopTestState, it: int) -> int { return shop_test_st.pack[it] } function fake_take(shop_test_st: mut ShopTestState, it: int, n: int) -> bool { if shop_test_st.pack[it] < n { return false } shop_test_st.pack[it] = shop_test_st.pack[it] - n return true } function fake_add(shop_test_st: mut ShopTestState, it: int, n: int) -> int { shop_test_st.pack[it] = shop_test_st.pack[it] + n return n } function fake_room(shop_test_st: ShopTestState, it: int) -> int { if it == JACKET { return 1 - shop_test_st.pack[it] } return 99 } bind ShopWorld { rep: fn fake_rep, day: fn fake_day, also_buys: fn fake_food } bind ShopPurse { money: fn fake_money, spend: fn fake_spend, earn: fn fake_earn } bind ShopPack { count: fn fake_count, take: fn fake_take, add: fn fake_add, room: fn fake_room } function ints(a: int, b: int, c: int, d: int) -> []int { let out = new []int if a >= 0 { push(out, a) } if b >= 0 { push(out, b) } if c >= 0 { push(out, c) } if d >= 0 { push(out, d) } return out } # the valley's own numbers: base prices and the sell classes function fresh(shop_st: mut ShopState, shop_test_st: mut ShopTestState) -> void { let prices = new []int let sell = new []float let p = ints(3, 6, 8, 9) let q = ints(10, 1, 6, 60) for i in 0 .. 4 { push(prices, p[i]) } for i in 0 .. 4 { push(prices, q[i]) } push(prices, 1) push(prices, 1) push(sell, RAW) push(sell, RAW) push(sell, RAW) push(sell, RAW) push(sell, MADE) push(sell, MADE) push(sell, BOUGHT) push(sell, BOUGHT) push(sell, RAW) push(sell, RAW) shop_config_items(shop_st, prices, sell) shop_clear_vendors(shop_st) shop_add_vendor(shop_st, ints(ROPE, JACKET, -1, -1), ints(WOOD, CHANTERELLE, -1, -1), -1) shop_add_vendor(shop_st, ints(BAIT, COOKED, -1, -1), ints(WOOD, PUFFBALL, BAIT, -1), BURNT) shop_add_vendor(shop_st, ints(BAIT, COOKED, GRUB, -1), ints(TROUT, COOKED, BAIT, GRUB), -1) shop_test_st.pack = new []int for i in 0 .. 10 { push(shop_test_st.pack, 0) } shop_test_st.rep = 0 shop_test_st.day = 1 shop_test_st.money = 0 shop_reset(shop_st) } function trades(shop_st: ShopState) -> []ShopTrade { return q_drain(shop_trades(shop_st)) } test "buying takes the price and puts one in the pack, and says so" (shop_st: mut ShopState, shop_test_st: mut ShopTestState) { fresh(shop_st, shop_test_st) shop_test_st.money = 100 shop_visit(shop_st, 0) let pr = shop_price_buy(shop_st, JACKET) expect(shop_buy(shop_st, JACKET)) expect_eq(shop_test_st.money, 100 - pr) expect_eq(shop_test_st.pack[JACKET], 1) expect(not shop_buy(shop_st, JACKET)) let ts = trades(shop_st) expect_eq(len(ts), 1) expect_eq(ts[0].what, SHOP_BOUGHT) expect_eq(ts[0].vendor, 0) expect_eq(ts[0].price, pr) } test "no money, nothing bought and nothing spent" (shop_st: mut ShopState, shop_test_st: mut ShopTestState) { fresh(shop_st, shop_test_st) shop_test_st.money = 3 expect(not shop_can_buy(shop_st, JACKET)) expect(not shop_buy(shop_st, JACKET)) expect_eq(shop_test_st.money, 3) expect_eq(len(trades(shop_st)), 0) } test "selling pays what the counter's vendor pays, only what the pack has" (shop_st: mut ShopState, shop_test_st: mut ShopTestState) { fresh(shop_st, shop_test_st) shop_test_st.pack[TROUT] = 3 shop_visit(shop_st, 2) let pr = shop_price_sell(shop_st, TROUT) expect_eq(shop_sell(shop_st, TROUT, 5), 3) expect_eq(shop_test_st.money, pr * 3) expect_eq(shop_test_st.pack[TROUT], 0) expect_eq(trades(shop_st)[0].n, 3) } test "a vendor buys its list and what the world adds, never what it refuses" (shop_st: mut ShopState, shop_test_st: mut ShopTestState) { fresh(shop_st, shop_test_st) expect(shop_buys(shop_st, 1, PUFFBALL)) expect(not shop_buys(shop_st, 1, TROUT)) shop_test_st.food_too = true expect(shop_buys(shop_st, 1, TROUT)) expect(not shop_buys(shop_st, 1, BURNT)) shop_test_st.pack[BURNT] = 1 shop_test_st.pack[TROUT] = 1 let s = shop_sellable(shop_st, 1) expect_eq(len(s), 1) expect_eq(s[0], TROUT) shop_visit(shop_st, 1) expect_eq(shop_sell(shop_st, BURNT, 1), 0) } test "the week's demand is the shop's own dice, and turns with the week" (shop_st: mut ShopState, shop_test_st: mut ShopTestState) { Random.seed(77) # the world's shared stream, which a price never moves fresh(shop_st, shop_test_st) shop_roll(shop_st) # the shop's tick rolls the week; asking only reads it let a = shop_wanted(shop_st, 0) expect(len(a) > 0) for k in 0 .. len(a) { expect(shop_buys(shop_st, 0, a[k])) } let again = shop_wanted(shop_st, 0) expect_eq(again[0], a[0]) shop_visit(shop_st, 0) let it = a[0] let plain = shop_price_sell_at(shop_st, -1, it) expect(shop_demand_mult(shop_st, 0, it) == 1.5) expect(shop_price_sell(shop_st, it) >= plain) let shared = Random.range(0, 999999) Random.seed(77) expect_eq(shared, Random.range(0, 999999)) var moved = false for w in 1 .. 12 { shop_test_st.day = 1 + w * 7 shop_roll(shop_st) let b = shop_wanted(shop_st, 0) if len(b) != len(a) or b[0] != a[0] { moved = true } } expect(moved) } test "a shop is not a fountain: nothing it stocks sells back for what it costs" (shop_st: mut ShopState, shop_test_st: mut ShopTestState) { fresh(shop_st, shop_test_st) for v in 0 .. shop_vendors(shop_st) { let st = shop_stock(shop_st, v) for k in 0 .. len(st) { for r in 0 .. 11 { shop_test_st.rep = r * 10 shop_set_want(shop_st, v, st[k]) let buy = shop_price_buy(shop_st, st[k]) let sell = shop_price_sell_at(shop_st, v, st[k]) expect(sell < buy) expect(sell >= 0) } } } } test "not even at a dollar: a penny item a vendor stocks fetches nothing" (shop_st: mut ShopState, shop_test_st: mut ShopTestState) { fresh(shop_st, shop_test_st) shop_test_st.rep = 100 shop_set_want(shop_st, 2, GRUB) expect_eq(shop_price_buy(shop_st, GRUB), 1) expect_eq(shop_price_sell_at(shop_st, 2, GRUB), 0) expect(shop_price_sell_at(shop_st, -1, GRUB) >= 1) } test "friction falls with standing and never inverts" (shop_st: mut ShopState, shop_test_st: mut ShopTestState) { fresh(shop_st, shop_test_st) var last_buy = 1000000 var last_sell = -1 for r in 0 .. 11 { shop_test_st.rep = r * 10 expect(shop_buy_factor(shop_st) > shop_sell_factor(shop_st)) let b = shop_price_buy(shop_st, JACKET) let s = shop_price_sell_at(shop_st, -1, COOKED) expect(b <= last_buy) expect(s >= last_sell) last_buy = b last_sell = s } shop_test_st.rep = 0 let b0 = shop_price_buy(shop_st, JACKET) shop_test_st.rep = 100 expect(shop_price_buy(shop_st, JACKET) < b0) shop_test_st.rep = 0 expect(float(b0) / 60.0 <= 1.25) } test "raw material keeps its order, and is not an income" (shop_st: mut ShopState, shop_test_st: mut ShopTestState) { fresh(shop_st, shop_test_st) for r in 0 .. 11 { shop_test_st.rep = r * 10 let w = shop_price_sell_at(shop_st, -1, WOOD) let ch = shop_price_sell_at(shop_st, -1, CHANTERELLE) let pf = shop_price_sell_at(shop_st, -1, PUFFBALL) let tr = shop_price_sell_at(shop_st, -1, TROUT) expect(w <= ch and ch <= pf and pf <= tr) expect(pf <= 8 / 2) } shop_test_st.rep = 0 expect(shop_price_sell_at(shop_st, -1, WOOD) < shop_price_sell_at(shop_st, -1, TROUT)) } }