KoTH: Simulator Politik
Saatnya pemilihan, dan tugas Anda adalah mengalahkan pesaing Anda dalam persaingan langsung! Anda berdua mencoba memenangkan kota berpenduduk 256 orang dalam grid 16x16. Saat ini, kota ini belum terbagi menjadi wilayah pemungutan suara, tapi di situlah keterampilan gerrymandering Anda masuk! Anda juga dapat berkampanye di area kota yang sewenang-wenang untuk mendapatkan dukungan.
Umum
Semua bot (fungsi JS) akan dijalankan melawan semua bot lain satu kali per game. Dalam setiap pertandingan, kota akan dimulai dengan semua pemilih bersikap netral. Gim ini akan melacak nomor untuk setiap pemilih, menentukan siapa yang mereka dukung. Dari perspektif bot tertentu, angka positif berarti pemilih akan memilih mereka, sedangkan angka negatif adalah suara lawan. Nol netral.
Kota ini terbagi menjadi 16 blok, yang semuanya berukuran 4x4 persegi. Daerah pemungutan suara terdiri dari satu blok atau lebih. Awalnya ada 16, dengan setiap blok memiliki wilayahnya sendiri.
Kedua bot mulai dari $ 100, dan dapat bergerak sekali per giliran. Gerakan ini dijalankan secara efektif secara bersamaan, sehingga tidak ada keuntungan putaran pertama. Gerakan terbagi dalam empat kategori:
- Kampanye: Hal ini akan menyebabkan setiap orang di wilayah tertentu meningkatkan dukungannya terhadap kandidat yang berkampanye. Jumlah yang berubah tergantung pada tetangga mereka.
- Polling: Ini akan mendapatkan dukungan setiap orang untuk kandidat di bidang tertentu.
- Penyuapan: Ini akan menyebabkan orang tertentu meningkatkan dukungannya terhadap kandidat.
- Penggabungan / Pemisahan Wilayah: Ini akan membentuk kembali wilayah pemungutan suara.
Di akhir setiap giliran, setelah kedua kandidat pindah, keduanya akan menerima $ 10.
Detail
Gerakan berikut diperbolehkan. Jika gerakan tidak valid diberikan (uang tidak cukup atau koordinat tidak valid), giliran bot akan dilewati.
Semua koordinat harus berada di dalam 0 <= n < 16, dan untuk pasangan kedua di area batas persegi panjang 0 <= n <= 16(karena ini eksklusif ).
campaign([x, y], [x, y]): Kampanye dalam batas yang ditentukan oleh dua pasangan koordinat- Biaya $ 1 per orang yang terpengaruh
- Setiap orang di dalam area tersebut akan mengubah dukungan mereka sesuai dengan aturan berikut:
- Untuk semua tetangga secara ortogonal (termasuk diagonal) yang berdekatan, tambahkan 0,1 dukungan untuk kandidat mana pun yang mendapat dukungan mayoritas (berbobot), atau 0,2 jika total dukungan mereka setidaknya 2 untuk kandidat itu
- Tambahkan 0,25 untuk kandidat yang sedang berkampanye
poll([x, y], [x, y]): Jajak pendapat dalam batas yang ditentukan oleh dua pasangan koordinat- Biaya $ 0,25 per orang yang disurvei, dibulatkan
- Pada giliran berikutnya, informasi berikut diberikan tentang setiap orang di area tersebut (setelah gerakan lawan):
- Dukungan mereka untuk calon pemungutan suara, di mana angka positif menunjukkan suara untuk mereka, dan angka negatif menunjukkan suara untuk lawan
bribe([x, y]): Suap orang di lokasi yang ditentukan oleh pasangan koordinat- Biaya setidaknya $ 5
- Untuk setiap kali seseorang dalam blok pemungutan suara ( bukan wilayah pemungutan suara) disuap, tambahkan $ 1
- Tambahkan hingga 3,5 dukungan untuk calon pemberi suap
- Untuk setiap kali seseorang dalam blok suara disuap, dukungan yang ditambahkan dikurangi sebesar 0,15
- Pada akhirnya, hal ini dapat menyebabkan suap seseorang di blok pemungutan suara mengurangi dukungan mereka terhadap kandidat
- Biaya setidaknya $ 5
merge([x, y], [x, y]): Gabungkan daerah pemilihan yang ditentukan oleh dua pasangan koordinat- Biaya $ 25 untuk setiap blok di wilayah yang baru terbentuk (satu blok gratis)
- Memerlukan wilayah yang berisi dua orang yang ditentukan untuk disentuh
- Perhatikan bahwa koordinat sesuai dengan orang, bukan blok. Untuk mereferensikan sebuah blok, kalikan saja koordinatnya dengan 4
unmerge([x, y]): Pisahkan wilayah pemungutan suara yang ditentukan oleh pasangan koordinat- Biaya $ 25 untuk setiap blok di wilayah tersebut
- Setiap blok di wilayah itu menjadi wilayahnya sendiri
Jika kedua bot mencoba menggabungkan atau memisahkan diri pada belokan yang sama (meskipun tidak akan mengganggu), kedua belokan akan dilewati dan keduanya tidak akan membayar apa pun. Pemindahan akan diproses dalam urutan berikut (urutan sisanya tidak penting):
- Suap
- Dukungan dari tetangga dalam berkampanye
- Dukungan dari kandidat dalam kampigning
Kemenangan
Di akhir setiap giliran, setelah kedua kandidat pindah, semua daerah akan ditambah suaranya. Setiap orang akan memilih salah satu kandidat atau netral, terlepas dari seberapa banyak (yaitu, skor +0,05 atau +30 akan identik di sini). Jika persyaratan berikut terpenuhi, pemilihan akan diadakan dan pemenang dipilih:
- Semua daerah terdiri dari kurang dari setengah pemilih netral
- Jumlah daerah yang memilih masing-masing calon tidak terikat
I / O
Semua bot harus dikirimkan dalam bentuk fungsi Javascript. Informasi berikut akan diberikan sebagai argumen untuk fungsi tersebut:
- Larik wilayah pemungutan suara, direpresentasikan sebagai objek dengan properti berikut:
blocks: Larik blok pemungutan suara, yang direpresentasikan sebagai koordinat[x, y]orang di kiri atas (seperti[4, 0]atau[12, 12])number_neutral: Jumlah orang di wilayah ini yang netralnumber_you: Jumlah orang di wilayah yang memilih botnumber_opponent: Jumlah orang di wilayah yang memilih lawan botabsolute_average: Nilai absolut rata-rata dukungan rakyat terhadap seorang calon- Angka yang lebih tinggi menunjukkan kampanye atau penyuapan biasanya kurang efektif
- Tepat 0 berarti setiap orang di wilayah tersebut netral
- Jumlah uang yang dimiliki bot
- Objek yang berisi hasil dari langkah terakhir (kosong kecuali jika itu adalah polling)
- Sebuah array
peopleakan berisi objek yang mewakili setiap orang yang disurvei:position: Koordinat orang, dengan format[x, y]region: ID bernomor dari wilayah tempat orang tersebut berada (indeks wilayah di argumen pertama)support: Angka yang menunjukkan apakah orang tersebut netral (0), memberi suara untuk bot (positif), atau memberi suara untuk lawan bot (negatif)
- Objek yang
amountsberisi properti berikut:number_neutral: Jumlah orang di wilayah ini yang netralnumber_you: Jumlah orang di wilayah yang memilih botnumber_opponent: Jumlah orang di wilayah yang memilih lawan botabsolute_average: Nilai absolut rata-rata dukungan rakyat terhadap seorang calon
- Sebuah array
- Sebuah objek yang dapat digunakan untuk penyimpanan di antara belokan (tetapi tidak di antara babak / permainan)
Untuk memindahkan, hasil salah satu fungsi di atas harus dikembalikan. Sebagai contoh:
{
"Example": (regions, money, result, storage) => {
storage.block = ((storage.block || 0) + 1) % 16;
return campaign(
[(storage.block / 4 | 0) * 4, (storage.block % 4) * 4],
[(storage.block / 4 | 0) * 4 + 4, (storage.block % 4) * 4 + 4]
);
}
}
Aturan
- Bot harus membutuhkan waktu yang cukup lama untuk dijalankan
- Bot harus bermain dengan adil, tidak memanipulasi pengontrol atau kiriman lain dengan cara yang tidak diperbolehkan di sini
- Setiap bot akan dijalankan melawan bot lainnya sekali, dengan pemenangnya adalah bot yang paling sering menang
- Jika terjadi seri, bot sebelumnya menang
- Nilai pengembalian tidak valid atau bot yang kesalahannya menganggap tidak ada gerakan untuk belokan
- Bot harus deterministik
Tantang ide dan proposal kotak pasir asli oleh HyperNeutrino.
Pengontrol: https://redwolfprograms.github.io/political-simulator
Ruang Obrolan: Di Sini
Tanggal Jatuh Tempo: Kamis, 5 November 2020, UTC Tengah Hari (8:00 EST)
Jawaban
Abotcus
Ternyata Anda tidak membutuhkan begitu banyak fitur mewah untuk menjadi sebaik atau lebih baik dari Randgrab! Abotcus mengambil area di awal, kemudian menerapkan rumus langsung untuk memberi bobot pada setiap blok dan memilih blok dengan bobot terbaik. Jika sepertinya ada beberapa hal yang tidak perlu di sana, itu karena saya berharap untuk menambahkan lebih banyak fitur mewah ... tapi saya tidak perlu ¯ \ _ (ツ) _ / ¯
(regions, money, result, storage) => {
if(money == 100) {return campaign([9,1],[15,15])}
var map = [[,,,],[,,,],[,,,],[,,,]];
var weights = [[,,,],[,,,],[,,,],[,,,]];
var blocks = [];
for(var r of regions) {
for(var b of r.blocks) {
map[b[1]/4][b[0]/4] = b;
weights[b[1]/4][b[0]/4] = weight(r.number_you, r.number_opponent, r.number_neutral, r.absolute_average)/r.blocks.length;
blocks.push([b, weights[b[1]/4][b[0]/4]]);
}
}
blocks.sort((a,b) => {
return b[1]-a[1];
});
var start_block = blocks[0][0];
console.log(blocks);
if(money >= 16) {
return campaign(start_block, [start_block[0]+4, start_block[1]+4]);
} else {
return campaign(start_block, [start_block[0]+3, start_block[1]+3]);
}
function weight(own, opp, neut, avg) {
var tot = own+opp+neut;
var cat = 0;
if(opp > tot/2) {
cat = 1;
} else if(own > tot/2) {
cat = 5;
}
return 1/(1+avg+cat);
}
}
```
Landgrab
Deskripsi singkat tentang strategi:
- Pada giliran pertama, lakukan kampanye besar di tengah dengan menggunakan semua uang awal.
- Sebaliknya jika ada daerah yang memiliki lebih dari 2 pemilih netral, lakukan kampanye di daerah dengan pemilih paling netral.
- Sebaliknya, jika ada wilayah di mana saya dan lawan saya memiliki jumlah pemilih yang sama, berkampanye di wilayah itu.
- Jika tidak, lakukan kampanye di wilayah dengan pemilih paling netral.
Hal ini bergantung pada fakta bahwa jauh lebih mudah untuk mengklaim pemilih netral daripada pemilih yang sudah akan memilih lawan Anda. Ia tidak tahu tentang gerrymandering, jadi mungkin tidak akan berhasil jika seseorang mengotak-atik daerah pemilihan, tetapi ini adalah permulaan.
(regions, money, result, storage) => {
if(money == 100) { return campaign([2, 2], [12, 12]); }
var best = regions[0];
var tied;
for (var i = 0; i < regions.length; i++) {
if(regions[i].number_neutral > best.number_neutral) {
best = regions[i];
}
if(regions[i].number_neutral == 0 && regions[i].number_you == regions[i].number_opponent) {
tied = regions[i];
}
}
var b;
if (tied && best.number_neutral > 2) {
b = tied.blocks[money % tied.blocks.length];
} else {
b = best.blocks[money % best.blocks.length];
}
if (money >= 16) {
return campaign(b, [b[0] + 4, b[1] + 4])
} else if (money % 2 == 0) {
return campaign(b, [b[0] + 3, b[1] + 3])
} else {
return campaign([b[0] + 1, b[1] + 1], [b[0] + 4, b[1] + 4])
}
}
Kebijakan Kiri
v1.1
Meraih sisi kiri peta, lalu gerrymander sisi kanan. Setelah mencapai itu, perlahan-lahan akan mengkampanyekan pihak kanan untuk menghindari kebuntuan.
(regions, money, result, storage) => {
storage.phase = storage.phase || 0;
storage.merge = storage.merge || 0;
if (storage.phase == 0){
storage.phase+=2;
return campaign([1,2],[5,14]);
}
if (storage.phase <= 6) {
var result = campaign([storage.phase,1],[storage.phase+1,15]);
if(money >= 14) storage.phase++;
return result;
}
if(storage.phase == 7){
if(storage.merge < 4){
var result;
if(money >= 25){
result = merge([9,(storage.merge*4)+1],[15,(storage.merge)*4+1])
storage.merge++;
storage.phase = 1
}
return result;
}
else if(regions.filter(r => r.blocks[0][0] <= 5)
.filter(r => r.number_you > r.number_opponent + r.number_neutral).length >= regions.length / 2){
area = (storage.merge % 4)*4;
if(money >= 18){
storage.merge++;
storage.phase = 1;
return campaign([9,area],[15,area+3]);
}
}
else{
storage.phase = 1;
var result = campaign([storage.phase,1],[storage.phase+1,15]);
if(money >= 14) storage.phase++;
return result;
}
}
}
Saat ini hanya mengalahkan bot contoh. Strateginya terlalu lambat untuk menang banyak.
Randgrab
Dimulai sebagai evolusi Landgrab untuk meningkatkan keacakan, kemudian perlahan menambahkan lebih banyak fitur hingga saat ini mengalahkan semua kontestan lainnya (Landgrab, Kebijakan Kiri 1.1, dan kampanye Greedy 9).
Fitur-fiturnya meliputi:
- Merampas lebih sedikit tanah pada awalnya untuk menghemat uang
- Merebut area yang berdekatan sekaligus jika kita punya cukup uang
- Batalkan prioritas area di mana kita sudah memiliki cukup keunggulan untuk menang dan lawan belum berkampanye
- Prioritaskan area di mana suara paling dekat ("status ayunan," jika Anda mau)
- Lebih banyak keacakan, termasuk memilih secara acak salah satu dari empat penjuru untuk diklaim saat melakukan 3x3
rVariabel yang cukup kacau (tapi tetap deterministik!) Yang mengontrol semua keacakan
Kelemahannya antara lain:
- Tidak cukup memprioritaskan keadaan ragu-ragu
- Tidak memanfaatkan fungsi non-kampanye apa pun
- Dapat terlempar oleh perubahan wilayah, meskipun hal ini telah diperbaiki sebagian
- Dapat dibuang oleh klaim yang tidak sejalan dengan batas dengan baik
(regions, money, result, storage) => {
if(money == 100) {return campaign([4, 4], [12, 12]);}
var r = money + money * regions.length;
regions.forEach(reg => r += reg.blocks[0][0] * reg.number_neutral + reg.blocks[0][1] * reg.number_you + money * reg.number_opponent + reg.blocks.length * reg.absolute_average);
r = Math.floor(r);
var tied = [];
var best = [regions[r % regions.length]];
var closest = [regions[(2*r) % regions.length]];
for (var i = 0; i < regions.length; i++) {
if(regions[i].number_neutral > best[0].number_neutral && !(regions[i].number_you > 8 && regions[i].number_opponent == 0)) {
best = [regions[i]];
} else if(regions[i].number_neutral == best[0].number_neutral && !(regions[i].number_you > 5 && regions[i].number_opponent == 0)) {
best.push(regions[i]);
}
if(regions[i].number_neutral == 0 && regions[i].number_you == regions[i].number_opponent) {
tied.push(regions[i]);
}
if(regions[i].number_opponent > regions[i].number_you && regions[i].absolute_average < closest[0].absolute_average) {
closest = [regions[i]];
} else if(regions[i].number_opponent > regions[i].number_you && regions[i].absolute_average == closest[0].absolute_average) {
closest.push(regions[i]);
}
}
var b;
var choice;
if (tied.length > 0 && best[0].number_neutral > 4) {
choice = tied;
} else {
choice = (best[0].number_neutral > 2 ? best : closest);
}
console.log(choice);
bt = choice[r % choice.length];
b = bt.blocks[r % bt.blocks.length];
var x = Math.floor(r/2) % 2;
var y = Math.floor(r/4) % 2;
if (money >= 18 && choice) {
for(var i = 0; i < choice.length; i++) {
for(var j = 0; j < choice[i].blocks.length; j++) {
var c = choice[i].blocks[j];
if(c[0] == b[0]-4 && c[1] == b[1]) {
return campaign([c[0]+1, c[1]], [b[0]+3, b[1]+3]);
} else if(c[0] == b[0]+4 && c[1] == b[1]) {
return campaign([b[0]+1, b[1]], [c[0]+3, c[1]+3]);
} else if(c[0] == b[0] && c[1] == b[1]-4) {
return campaign([c[0], c[1]+1], [b[0]+3, b[1]+3]);
} else if(c[0] == b[0] && c[1] == b[1]+4) {
return campaign([b[0], b[1]+1], [c[0]+3, c[1]+3]);
}
}
}
}
if (money >= 16) {
return campaign(b, [b[0] + 4, b[1] + 4]);
} else {
return campaign([b[0] + x, b[1] + y], [b[0] + 3 + x, b[1] + 3 + y]);
}
}
Kampanye serakah 9 per wilayah
- Setiap giliran, kami memiliki setidaknya $ 10. Ini memungkinkan untuk mengkampanyekan area 3x3.
- Karena kami hanya membutuhkan 9 gird di setiap wilayah. Kami hanya mengkampanyekan 3x3 teratas tetapi menyerahkan yang di kanan atau bawah.
- Setiap kesempatan, kami memilih posisi kampanye serakah: Semakin banyak wilayah mendukung kami di giliran berikutnya, Semakin baik.
(regions, money, result, storage) => {
const turn = storage.turn = storage.turn + 1 || 1;
const gh = money < 21 ? 1 : 2;
const h = 4 * gh - 1;
const gw = 77 <= money ? 3 : 49 <= money ? 2 : 1;
const w = 4 * gw - 1;
const candidate = [];
for (let i = 0; i <= 4 - gw; i++) {
for (let j = 0; j <= 4 - gh; j++) {
let s = gw * gh;
for (let k = 0; k < gw; k++) {
for (let l = 0; l < gh; l++) {
let bx = i + k, by = j + l;
let region = regions.find(r => r.blocks.some(block => block == [bx * 4, by * 4] + ''));
let { number_neutral: n, number_opponent: o, number_you: y } = region;
if (y <= o) {
if (n + y > o) s += 1;
if (n + y == o) s += 0.5;
if (n + y < o) s += 0.5 ** (o - y - n / 2);
} else {
if (n + o > y) s += 0.5;
if (n + o < y) s += -(0.5 ** (y - o - n / 2));
if (n + o == y) s += 0.25;
}
}
}
candidate.push({ s, x: i, y: j });
}
}
const ts = Math.max(...candidate.map(c => c.s));
const best = candidate.filter(c => c.s === ts);
const { x: tx, y: ty } = best[turn % best.length];
return campaign([tx * 4, ty * 4], [tx * 4 + w, ty * 4 + h]);
}
Agenda Liberal
Penerus spiritual Kebijakan Kiri. Kampanye seluruh sisi kiri belok satu, lalu gerrymander kedua blok kanan atas bersama-sama, lalu kembali untuk melindungi sisi kiri. Setelah sangat yakin ia telah menutupi bagian kiri, ia bergerak ke kanan.
(regions, money, result, storage) => {
storage.merge = (storage.merge||0)
if(money >= 100){
return campaign([1,1],[7,15])
}
else {
var map = [];
regions.forEach(reg => {
reg.blocks.forEach(b => {
map.push({
"pos": b,
"abs_avg":reg.absolute_average,
"num_you":reg.number_you / reg.blocks.length,
"num_opp":reg.number_opponent / reg.blocks.length,
"num_neu":reg.number_neutral / reg.blocks.length,
"won": (reg.number_neutral>=reg.number_you+reg.number_opponent) ? 0 : reg.number_you/ reg.blocks.length - reg.number_opponent / reg.blocks.length
});
});
});
map = map.sort((a,b)=>a.abs_avg-b.abs_avg).sort((a,b)=>a.won - b.won);
var leftmap = map.filter(b=>b.pos[0]<8)
if(money >= 25){
if(storage.merge == 0){
storage.merge++
return merge([8,0],[12,0])
}
return campaign([leftmap[0].pos[0],leftmap[0].pos[1]],[leftmap[0].pos[0]+4,leftmap[0].pos[1] + 4])
}
if(leftmap.every(b=> b.abs_avg > 3)) return campTarget3by3(map[0].pos).filter(b=>b.won<1)
return campTarget3by3(leftmap[0].pos)
}
function campTarget3by3(pos){
var a1,a2,b1,b2
if(pos[0]==0){
a1 = 1
b1 = 4
}
else {
a1 = pos[0]
b1 = pos[0] + 3
}
if(pos[1]==0){
a2 = 1
b2 = 4
}
else {
a2 = pos[1]
b2 = pos[1] + 3
}
return campaign([a1,a2],[b1,b2])
}
}
Ironisnya, itu satu-satunya hal yang dapat dikalahkan oleh Kebijakan Kiri!