144 lines
3.6 KiB
TypeScript
144 lines
3.6 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
normalAuctionHandBlock,
|
|
quoteNormalAuctionBid,
|
|
quoteNormalAuctionNext,
|
|
} from '../game/normal-auction';
|
|
|
|
void test('a hand filled after an ordinary lot opens cannot bid, while faction-specific free capacity remains usable', () => {
|
|
assert.equal(normalAuctionHandBlock(3, 4), null);
|
|
assert.match(normalAuctionHandBlock(4, 4)!, /hand is full; pass/);
|
|
assert.equal(normalAuctionHandBlock(7, 8), null);
|
|
assert.match(normalAuctionHandBlock(8, 8)!, /hand is full; pass/);
|
|
assert.match(normalAuctionHandBlock(5, 4)!, /hand is full; pass/);
|
|
});
|
|
|
|
void test('ordinary auction follows table order and skips full-hand seats and the high bidder', () => {
|
|
assert.deepEqual(
|
|
quoteNormalAuctionNext({
|
|
order: ['a', 'full', 'b', 'c'],
|
|
eligible: ['a', 'b', 'c'],
|
|
active: 'a',
|
|
bid: 3,
|
|
bidder: 'a',
|
|
passed: [],
|
|
}),
|
|
{ kind: 'bid', player: 'b' },
|
|
);
|
|
assert.deepEqual(
|
|
quoteNormalAuctionNext({
|
|
order: ['a', 'full', 'b', 'c'],
|
|
eligible: ['a', 'b', 'c'],
|
|
active: 'c',
|
|
bid: 4,
|
|
bidder: 'b',
|
|
passed: [],
|
|
}),
|
|
{ kind: 'bid', player: 'a' },
|
|
);
|
|
});
|
|
|
|
void test('a cleared pass list lets a prior passer reenter after a new high bid', () => {
|
|
assert.deepEqual(
|
|
quoteNormalAuctionNext({
|
|
order: ['a', 'b', 'c'],
|
|
eligible: ['a', 'b', 'c'],
|
|
active: 'b',
|
|
bid: 3,
|
|
bidder: 'a',
|
|
passed: ['b'],
|
|
}),
|
|
{ kind: 'bid', player: 'c' },
|
|
);
|
|
assert.deepEqual(
|
|
quoteNormalAuctionNext({
|
|
order: ['a', 'b', 'c'],
|
|
eligible: ['a', 'b', 'c'],
|
|
active: 'c',
|
|
bid: 5,
|
|
bidder: 'c',
|
|
passed: [],
|
|
}),
|
|
{ kind: 'bid', player: 'a' },
|
|
);
|
|
assert.deepEqual(
|
|
quoteNormalAuctionNext({
|
|
order: ['a', 'b', 'c'],
|
|
eligible: ['a', 'b', 'c'],
|
|
active: 'a',
|
|
bid: 5,
|
|
bidder: 'c',
|
|
passed: ['a'],
|
|
}),
|
|
{ kind: 'bid', player: 'b' },
|
|
);
|
|
});
|
|
|
|
void test('all-pass and completed winning rounds terminate without another bid', () => {
|
|
assert.deepEqual(
|
|
quoteNormalAuctionNext({
|
|
order: ['a', 'b', 'c'],
|
|
eligible: ['a', 'b', 'c'],
|
|
active: 'c',
|
|
bid: 0,
|
|
bidder: null,
|
|
passed: ['a', 'b', 'c'],
|
|
}),
|
|
{ kind: 'unbid' },
|
|
);
|
|
assert.deepEqual(
|
|
quoteNormalAuctionNext({
|
|
order: ['a', 'b', 'c'],
|
|
eligible: ['a', 'b', 'c'],
|
|
active: 'c',
|
|
bid: 4,
|
|
bidder: 'a',
|
|
passed: ['b', 'c'],
|
|
}),
|
|
{ kind: 'payment', player: 'a' },
|
|
);
|
|
});
|
|
|
|
void test('empty eligibility is unbid and malformed order fails closed', () => {
|
|
assert.deepEqual(
|
|
quoteNormalAuctionNext({
|
|
order: ['a', 'b'],
|
|
eligible: [],
|
|
active: 'a',
|
|
bid: 0,
|
|
bidder: null,
|
|
passed: [],
|
|
}),
|
|
{ kind: 'unbid' },
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
quoteNormalAuctionNext({
|
|
order: ['a', 'b'],
|
|
eligible: ['missing'],
|
|
active: 'a',
|
|
bid: 0,
|
|
bidder: null,
|
|
passed: [],
|
|
}),
|
|
/no eligible next bidder/,
|
|
);
|
|
});
|
|
|
|
void test('ordinary bid quote accepts only safe integers within the funded range', () => {
|
|
assert.deepEqual(
|
|
quoteNormalAuctionBid({ currentBid: 3, amount: 4, maximum: 8 }),
|
|
{ ok: true, amount: 4 },
|
|
);
|
|
assert.deepEqual(
|
|
quoteNormalAuctionBid({ currentBid: 3, amount: 8, maximum: 8 }),
|
|
{ ok: true, amount: 8 },
|
|
);
|
|
for (const amount of [3, 9, 4.5, Number.NaN, Number.POSITIVE_INFINITY, '4'])
|
|
assert.deepEqual(
|
|
quoteNormalAuctionBid({ currentBid: 3, amount, maximum: 8 }),
|
|
{ ok: false, reason: 'Bid must be an integer from 4 to 8.' },
|
|
);
|
|
});
|