5
4
A few days ago, I came up with a fun card game to play with my friends. We were having fun playing it, when I thought, "Why not make this a KoTH?" So, here it is!
Overview
In this game, the objective is to get the most points. Your bot starts with 0 points and 20 energy. Every turn (500 in a game), both bots play one card. Some earn you points, some take the opponent's points.
Cards
A=Add 5 points to your score [Costs 0.1 energy]
R=Remove 5 points from opponent [Costs 0.1 energy]
H=Half your next score [Costs 1 energy]
Y=Half opponent's next score [Costs 1 energy]
D=Double your next score [Costs 2 energy]
T=Double opponent's next score [Costs 2 energy]
N=Negate your next score [Costs 3 energy]
O=Negate opponent's next score [Costs 3 energy]
S=Shield for 5 turns [Costs 15 energy]
X=Take five energy from opponent [Gives opponent 10 points]
E=Refill five energy [Costs 10 points]
How it works
First how would halving your next score or doubling your opponent's next score come in handy? Well, imagine you get 5 points taken away on your next turn. Instead, 2.5 points get taken away. Cool, right? Or, negating your next score would give you 5. If you think your opponent will give themself points, negate their next move!
The order of operations for modifying point values is:
Add all positive or negative point changes
Half if necessary
Double if necessary
Negate if necessary
If shielding and result is negative, change to 0
Attempting to lower the opponent's energy past 0 does not work. There is no upper limit to energy, or lower limit to points. Energy reducing cards are played before other cards, so if Bot A with 17 energy runs shield and Bot B runs take 5 energy, Bot A cannot shield.
Creating a bot
In Javascript, create a new function with whatever name you wish. This function should take 3 parameters:
Array of Numbers (Your coins, Opponent's coins)
Array of Numbers (Your energy, Opponent's energy)
Object (Use for storage between rounds)
Note that there is no way of knowing what cards have been played. You must teach the bot to figure it out by itself!
The way you select a card is by return
ing a string, containing the letter of the card in uppercase or lowercase. Note that Standard Loopholes (obviously) aren't allowed. If any other value is returned, your bot just does nothing.
Scoring
Every bot will be run against each other bot once. The loser will get 0 points, anbd the winner will get the difference in points added to its total score (Between all rounds). The top 8 will compete in a tournament. In other words, they will each fight one other bot, the four winners will fight another bot, and the two remaining bots will fight for first.
Controller
var botDataList = [
{
name: "",
desc: "",
run: function(){
}
}, {
name: "",
desc: "",
run: function(){
}
}
];
function playGame() {
botSetup();
for (var i = 0; i < 500; i++) {
runBots();
}
var botA = botDataList[0];
var botB = botDataList[1];
console.log("Bot " + botA.name + ": " + botA.points);
console.log("Bot " + botB.name + ": " + botB.points);
}
function botSetup() {
for (var b, i = 0; i < 2; i++) {
b = botDataList[i];
b.points = 0;
b.energy = 20;
b.storage = {};
b.affectAdd = [];
b.affectAct = [];
b.shield = 0;
}
}
function runBots() {
var botA = botDataList[0];
var botB = botDataList[1];
var resA = botA.run([botDataList[0].points, botDataList[1].points], [botDataList[0].energy, botDataList[1].energy], botDataList[0].storage).toLowerCase();
var resB = botB.run([botDataList[1].points, botDataList[0].points], [botDataList[1].energy, botDataList[0].energy], botDataList[1].storage).toLowerCase();
var modA = 0;
var modB = 0;
if (resA == 'a' && botA.energy >= 0.1) {
botA.energy -= 0.1;
modA += 5;
} else if (resA == 'r' && botA.energy >= 0.1) {
botA.energy -= 0.1;
modB -= 5;
} else if (resA == 'h' && botA.energy >= 1) {
botA.energy -= 1;
botA.affectAdd.push('h');
} else if (resA == 'y' && botA.energy >= 1) {
botA.energy -= 1;
botB.affectAdd.push('h');
} else if (resA == 'd' && botA.energy >= 2) {
botA.energy -= 2;
botA.affectAdd.push('d');
} else if (resA == 't' && botA.energy >= 2) {
botA.energy -= 2;
botB.affectAdd.push('d');
} else if (resA == 'n' && botA.energy >= 3) {
botA.energy -= 3;
botA.affectAdd.push('n');
} else if (resA == 'o' && botA.energy >= 3) {
botA.energy -= 3;
botB.affectAdd.push('n');
} else if (resA == 's' && botA.energy >= 15) {
botA.energy -= 15;
botA.shield += 5;
} else if (resA == 'x') {
modB += 10;
botB.energy = (botB.energy >= 5) ? botB.energy - 5 : 0;
} else if (resA == 'e' && botA.points >= 10) {
modA -= 10;
botA.energy += 5;
}
if (resB == 'a' && botB.energy >= 0.1) {
botB.energy -= 0.1;
modB += 5;
} else if (resB == 'r' && botB.energy >= 0.1) {
botB.energy -= 0.1;
modA -= 5;
} else if (resB == 'h' && botB.energy >= 1) {
botB.energy -= 1;
botB.affectAdd.push('h');
} else if (resB == 'y' && botB.energy >= 1) {
botB.energy -= 1;
botA.affectAdd.push('h');
} else if (resB == 'd' && botB.energy >= 2) {
botB.energy -= 2;
botB.affectAdd.push('d');
} else if (resB == 't' && botB.energy >= 2) {
botB.energy -= 2;
botA.affectAdd.push('d');
} else if (resB == 'n' && botB.energy >= 3) {
botB.energy -= 3;
botB.affectAdd.push('n');
} else if (resB == 'o' && botB.energy >= 3) {
botB.energy -= 3;
botA.affectAdd.push('n');
} else if (resB == 's' && botB.energy >= 15) {
botB.energy -= 15;
botB.shield += 5;
} else if (resB == 'x') {
modA += 10;
botA.energy = (botA.energy >= 5) ? botA.energy - 5 : 0;
} else if (resB == 'e' && botB.points >= 10) {
modB -= 10;
botB.energy += 5;
}
if (botA.affectAct.includes('h')) {
modA *= 0.5;
}
if (botA.affectAct.includes('d')) {
modA *= 2;
}
if (botA.affectAct.includes('n')) {
modA *= -1;
}
if (botA.shield > 0) {
modA = (modA < 0) ? 0 : modA;
botA.shield--;
}
botA.points += modA;
botA.affectAct = botA.affectAdd;
botA.affectAdd = [];
if (botB.affectAct.includes('h')) {
modB *= 0.5;
}
if (botB.affectAct.includes('d')) {
modB *= 2;
}
if (botB.affectAct.includes('n')) {
modB *= -1;
}
if (botB.shield > 0) {
modB = (modB < 0) ? 0 : modB;
botB.shield--;
}
botB.points += modB;
botB.affectAct = botB.affectAdd;
botB.affectAdd = [];
}
/* A=Add 5 points to your score [Costs 0.1 energy]
R=Remove 5 points from opponent [Costs 0.1 energy]
H=Half your next score [Costs 1 energy]
Y=Half opponent's next score [Costs 1 energy]
D=Double your next score [Costs 2 energy]
T=Double opponent's next score [Costs 2 energy]
N=Negate your next score [Costs 3 energy]
O=Negate opponent's next score [Costs 3 energy]
S=Shield for 5 turns [Costs 15 energy]
X=Take five energy from opponent [Gives opponent 10 points]
E=Refill five energy [Takes 10 points] */
Is there an upper limit to points? – HyperNeutrino – 2018-09-06T20:19:05.510
@HyperNeutrino No, but the max possible in a 500-turn game is 2500 – Redwolf Programs – 2018-09-06T20:20:24.347
2Being a card game enthusiast, this challenge being restricted to Javascript makes me sad. – J. Sallé – 2018-09-06T20:46:14.303
@J.Sallé Program it in another language, and I'll do like I did in my last challenge and translate. I don't want to restrict people's ability to enter the challenge, it just makes it easier on my part if they;re all in one language. – Redwolf Programs – 2018-09-06T20:47:13.183
Shielding protects you for 5 turns. If you're already shielded, should this add to the duration? The controller currently just refreshes the count to 5. – Veskah – 2018-09-06T21:41:28.363
@Veskah Huh, you're right. It should indeed! – Redwolf Programs – 2018-09-06T22:10:24.567
This is a bit unclear - Can I choose any card from the 11 that are available to play each turn? – Quintec – 2018-09-07T00:03:24.813
@Quintec Yes you can. I think it might be better to phrase them as moves, rather than cards. – Don Thousand – 2018-09-07T00:17:42.137
@RushabhMehta Originally you would have 5 "cards" to pick from randomly, but I removed that part so that there would be less randomness – Redwolf Programs – 2018-09-07T01:25:13.807
@RedwolfPrograms Does "E", refilling 5 energy and costing 10 points, count as a -10 score? – Quintec – 2018-09-07T02:33:15.230
3This could be really interesting as more than a 1v1 contest – MickyT – 2018-09-07T02:41:16.263
Are you only playing a single game? And are games actually 1v1? Because the controller only plays games between the first two players, nobody else. – Nathan Merrill – 2018-09-07T02:49:35.020
There's a typo in the controller:
} else if (resB == 'r' && BotB.energy >= 0.1) {
theBotB
should bebotB
. – Omegastick – 2018-09-07T04:33:32.2005What is the objective of the game? Is it to get the most points, summed over many rounds? Beat the opponent? Something else? I'm going to have to downvote until this is clarified. – isaacg – 2018-09-07T05:50:50.713
Attempting to lower the opponent's score past 0 does not work. There is no upper limit to energy, or lower limit to points. -- could you clarify? – Jonathan Frech – 2018-09-07T06:28:23.680
I'm going through the controller some more, and it's completely broken. There are so many things that don't work. None of the 'affects[sic]' work because you add them to
affectAdd
and read them fromaffectAct
. When adding up the score modifier, sometimes you incrementmodA
ormodB
and sometimesmodA.points
ormodB.points
. Did you even try running this before you posted it? – Omegastick – 2018-09-07T06:55:57.410@Omegastick
affectAct
andaffectAdd
things are OK because the effects have to be applied at the next turn, not this turn. But you're right aboutmodA.points
andmodB.points
. They should definitely bemodA
andmodB
instead. The rest looks fine to me; at least I can run my 2500-scoring bot against the "greedy" without problems after fixingmodX
myself. – Bubbler – 2018-09-07T07:42:21.783Oh, there's also
modB.energy
. That one should bebotB.energy
instead. – Bubbler – 2018-09-07T07:43:59.693Does doubling your next score stack? As in can I double for two rounds to get x4? – Barbarian772 – 2018-09-07T07:46:36.673
@RedwolfPrograms The controller has some incredibly silly code duplication. I fixed it up a bit, but I'm sure there are ways to make this even better. (I haven't tested for correctness, though, since I just threw this together in 5 minutes. I can't imagine screwing anything up, but treat this as more of a proof of concept rather than a replacement anyway. (This is in response Redwolf's "I needed to have 1
– Alion – 2018-09-07T10:01:57.150if
for every card for both bots" - you didn't ;) ))4What precisely does "next score" mean? What does shield do? In "Energy reducing cards are played before other cards", surely every card except
X
is an energy reducing card? IMO this question still needs a few days in the sandbox. – Peter Taylor – 2018-09-07T10:07:30.270Should it be possible for negative scores to happen? I've found a lot of examples when running Doom vs Greedier... – Alion – 2018-09-07T10:28:04.967
@RedwolfPrograms Also, what if my move renders the opponent with not enough score to execute his move? Whose move executes first? – Don Thousand – 2018-09-07T11:46:06.913
@RedwolfPrograms "Attempting to lower the opponent's score past 0 does not work." Doom disagrees - OX on an opponent with 0 score results in a negative score... – Alion – 2018-09-07T11:55:12.573
Speaking of which, I think @Bubbler 's Doom has successfully doomed this challenge to failure - I don't think you can win against it. – Alion – 2018-09-07T11:58:45.670
@J.Sallé Or someone else. If the bot is interesting enough. /// (or just write a simple other-language wrapper...) – user202729 – 2018-09-07T15:35:19.360
@PeterTaylor
X
is the energy reducing card. – Redwolf Programs – 2018-09-07T21:48:03.260@Barbarian772 It does stack. You could also end up with x1/4 – Redwolf Programs – 2018-09-07T21:48:28.267
@RushabhMehta I'm going to fix that part of the code. Sorry, didn't test it more than a few simple games – Redwolf Programs – 2018-09-07T21:49:13.553
Could be way more interesting if the game was more card-game-like, i.e. design a deck and make a bot play with it strategically. Somewhat reminds me of Castle Wars series.
– Bubbler – 2018-09-08T05:58:20.140@Bubbler I would love a Castle Wars-esque KOTH! – Alion – 2018-09-09T14:00:33.213
@RedwolfPrograms you might havemissed this question: Does "E", refilling 5 energy and costing 10 points, count as a -10 score? – Quintec – 2018-09-09T14:42:05.130
@Quintec yes, so
O
orN
can cause you to get points from it – Redwolf Programs – 2018-09-10T19:10:38.917playing only a single round guarantees the winner will be chosen primarily by luck. why not play each matchup 1000x times and use avg difference? this is the whole benefit of having computer programs rather than humans playing... – Jonah – 2019-02-17T15:36:14.327