Currently, I am in the process of developing a cost function for a game where players are given a set of resources in their hand. The resources can be categorized into different types such as Fire, Air, Water, Earth, Good, Evil, Law, Chaos, and Void.
These resources are further divided into two categories: Elements (Fire, Air, Water, Earth) and Philosophies (Good, Evil, Law, Chaos). In this game, player actions come with a cost that can consist of any combination of resources or categories.
- For example:
[Fire, Air, Evil, Philosophy, Philosophy]
To pay a cost, players need to select resources from their hand that correspond to the specified cost. They can use resources from the same category to fulfill category costs, and there is also a wildcard resource called Void that can be used to pay for any other resource.
- A valid payment for the example cost mentioned above could be
[Void, Void, Air, Good, Good]
However, my current evaluation function is struggling to handle substitutions of categories or the Void resource. It currently only checks for exact equivalence:
/** Returns true if the resources currently selected by the player
* satisfy the @cost of the option selected */
evaluatePayment(cost: Resource[]): boolean {
let isValidPayment: boolean = true;
if (cost) {
const playerSelectedResources: Resource[] = this.playerHand
.filter(r => r.isSelected)
.map(hr => hr.type);
// Check that selected resources cover cost
const missingCost = cost.filter(r => playerSelectedResources.indexOf(r));
// Check that no additional resources are selected
const excessPaid = playerSelectedResources.filter(r => cost.indexOf(r));
if (missingCost.length > 0 || excessPaid.length > 0) {
isValidPayment = false;
}
}
return isCostSelected;
}
After receiving feedback, I have restructured the problem as follows:
enum Resource {
FIRE,
AIR,
WATER,
EARTH,
GOOD,
EVIL,
LAW,
CHAOS,
VOID,
ELEMENT,
PHILOSOPHY,
ANY
}
export const ElementResources: Resource[] = [Resource.FIRE, Resource.AIR, Resource.WATER, Resource.EARTH];
export const PhilosophyResources: Resource[] = [Resource.GOOD, Resource.EVIL, Resource.LAW, Resource.CHAOS];
function isValidExactPayment(cost: Resource[], payment: Resource[]): boolean {
/** Logic here
* Returns True if payment matches cost exactly,
* according to the rules above */
}
Further examples are provided for better understanding:
/** Example 1 */
const cost: Resource[] = [Resource.WATER, Resource, EVIL];
isValidExactPayment(cost, [Resource.WATER, Resource.EVIL]); // true
isValidExactPayment(cost, [Resource.EVIL, Resource.VOID]); // true
isValidExactPayment(cost, [Resource.VOID, Resource.EVIL]); // true, order should not matter
isValidExactPayment(cost, [Resource.WATER, Resource.VOID]); // true
...
(Additional examples were provided but omitted for brevity)
...
I am facing challenges in implementing a more advanced cost evaluation function due to the complexity of the requirements.