aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAiden Woodruff <woodra@rpi.edu>2025-11-14 11:36:02 -0500
committerAiden Woodruff <woodra@rpi.edu>2025-11-14 11:36:02 -0500
commit81b45c03ed83658f80b32a46d9f51e298fa8e2f1 (patch)
tree31a33cdeb8ef832c41e2d237dd321716ec583276 /src
parentc4b6d2dad64b8136b99fa7358ec141d4a46ff4e9 (diff)
downloadtipping-points-81b45c03ed83658f80b32a46d9f51e298fa8e2f1.tar.gz
tipping-points-81b45c03ed83658f80b32a46d9f51e298fa8e2f1.tar.bz2
tipping-points-81b45c03ed83658f80b32a46d9f51e298fa8e2f1.zip
created encapsulated NameGame class
- src/CMakeLists.txt: add NameGame.cc. - src/NameGame.cc: move best_move, update_memory, run round, round into this class and file. - move begin/end for TVec here. - add initGraph and initMemory for changing sizes. - src/NameGame.h: add class declaration. - main.cc: move all functions out of here. - use one object and re initialize memory for different committed minority sizes. Signed-off-by: Aiden Woodruff <woodra@rpi.edu>
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/NameGame.cc130
-rw-r--r--src/NameGame.h34
-rw-r--r--src/main.cc111
4 files changed, 173 insertions, 107 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c66a468..536e475 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,2 +1,5 @@
1add_executable(main main.cc) 1add_executable(main
2 main.cc
3 NameGame.cc
4)
2target_link_libraries(main PRIVATE SNAP::SNAP) 5target_link_libraries(main PRIVATE SNAP::SNAP)
diff --git a/src/NameGame.cc b/src/NameGame.cc
new file mode 100644
index 0000000..8ea3dcf
--- /dev/null
+++ b/src/NameGame.cc
@@ -0,0 +1,130 @@
1#include <iostream>
2#include <map>
3#include <stdexcept>
4#include <vector>
5
6#include "NameGame.h"
7
8template<typename TVal, typename TSizeTy>
9auto begin(const TVec<TVal, TSizeTy>& v) -> decltype(v.BegI()) {
10 return v.BegI();
11}
12
13template<typename TVal, typename TSizeTy>
14auto end(const TVec<TVal, TSizeTy>& v) -> decltype(v.EndI()) {
15 return v.EndI();
16}
17
18namespace tp {
19
20NameGame::NameGame(int group_size, int cm_size, int memory) :
21 gsize(group_size), cmsize(cm_size), memlen(memory),
22 rng(std::random_device{}()), dist(0, group_size - 1),
23 graph(group_size, group_size * group_size)
24{
25 if (group_size <= 0)
26 throw std::invalid_argument("NameGame invalid group_size");
27 if (cm_size < 0)
28 throw std::invalid_argument("NameGame invalid cm_size");
29 if (cm_size > group_size)
30 throw std::invalid_argument("NameGame cm_size > group_size");
31 if (memory <= 0)
32 throw std::invalid_argument("NameGame memory <= 0");
33 initGraph();
34}
35
36void NameGame::setCMsize(int cm_size) {
37 if (cm_size <= 0 || cm_size > gsize)
38 throw std::invalid_argument("NameGame::setCMsize invalid cm_size");
39 cmsize = cm_size;
40}
41
42void NameGame::initGraph() {
43 // Generate complete graph.
44 for (int i = 0; i < gsize; ++i) graph.AddNode(i, {false, {}});
45 for (int i = 0; i < gsize; ++i)
46 for (int j = 0; j < gsize; ++j)
47 if (i != j) graph.AddEdge(i, j);
48 initMemory();
49}
50
51void NameGame::initMemory() {
52 std::vector<int> ids(gsize);
53 std::iota(ids.begin(), ids.end(), 0);
54 // Set committed minority.
55 for (int i = 0; i < cmsize; ++i) {
56 int r = dist(rng) % ids.size();
57 int rId = ids[r];
58 auto& rDat = graph.GetNDat(rId);
59 rDat.Val1 = true;
60 rDat.Val2.Clr();
61 rDat.Val2.Add(2);
62 ids.erase(ids.begin() + r);
63 }
64 // Establish norm (fully memory of strategy 1) for remaining nodes.
65 for (int id : ids) {
66 auto& d = graph.GetNDat(id);
67 d.Val1 = false;
68 d.Val2.Clr();
69 while (d.Val2.Len() < memlen) d.Val2.Add(1);
70 }
71}
72
73void NameGame::run(int rounds) {
74 if (rounds < 0) throw std::invalid_argument("NameGame::run rounds < 0");
75 for (int i = 0; i < rounds; ++i) runRound();
76 // Track all plays and plays by non-CM.
77 // Report average plays.
78}
79
80void NameGame::runRound() {
81 // Select random speaker and hearer.
82 auto speaker = graph.GetNI(dist(rng));
83 auto hearer = graph.GetNI(
84 speaker.GetNbrNId(dist(rng) % speaker.GetDeg())
85 );
86
87 // Speaker chooses best strategy.
88 int s_strategy = best_move(speaker.GetId());
89 std::cout << "speaker (" << speaker.GetId() << " "
90 << std::boolalpha << speaker().Val1
91 << ") chose: " << s_strategy << std::endl;
92
93 // Listener updates memory.
94 update_memory(hearer.GetId(), s_strategy);
95
96 // Listener chooses best strategy.
97 int h_strategy = best_move(hearer.GetId());
98 std::cout << "hearer (" << hearer.GetId() << " "
99 << std::boolalpha << hearer().Val1
100 << ") chose: " << h_strategy << std::endl;
101
102 // Speaker updates memory.
103 update_memory(speaker.GetId(), s_strategy);
104}
105
106int NameGame::best_move(int nId) const {
107 const auto& mem = graph.GetNDat(nId).Val2;
108 std::map<int, int> votes;
109 for (const auto& v : mem) {
110 if (votes.count(v)) votes[v] += 1;
111 else votes[v] = 1;
112 }
113 std::map<int, float> fractions;
114 for (auto kv : votes) fractions[kv.first] = kv.second * 1./mem.Len();
115 // Simple majority vote:
116 for (auto kv : fractions)
117 if (kv.second >= 1./mem.Len()) return kv.first;
118 return 0;
119}
120
121void NameGame::update_memory(int nId, int strategy) {
122 auto d = graph.GetNDat(nId);
123 if (!d.Val1) {
124 auto& mem = d.Val2;
125 mem.Ins(0, strategy);
126 mem.Trunc(memlen);
127 }
128}
129
130} // namespace tp
diff --git a/src/NameGame.h b/src/NameGame.h
new file mode 100644
index 0000000..8a817ce
--- /dev/null
+++ b/src/NameGame.h
@@ -0,0 +1,34 @@
1#ifndef TIPPING_POINTS_NAMEGAME_H
2#define TIPPING_POINTS_NAMEGAME_H
3
4#include <random>
5
6#include <Snap.h>
7
8namespace tp {
9
10class NameGame {
11public:
12 NameGame(int group_size, int cm_size, int memory = 12);
13
14 void setCMsize(int cm_size);
15
16 void initGraph();
17 void initMemory();
18 void run(int rounds);
19
20protected:
21 int best_move(int nId) const;
22 void update_memory(int nId, int strategy);
23
24private:
25 void runRound();
26 int gsize, cmsize, memlen;
27 std::mt19937 rng;
28 std::uniform_int_distribution<> dist;
29 TNodeNet<TPair<TBool, TIntV>> graph;
30}; // class NameGame
31
32} // namespace tp
33
34#endif // TIPPING_POINTS_NAMEGAME_H
diff --git a/src/main.cc b/src/main.cc
index 0f391eb..e41f4ff 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -1,119 +1,18 @@
1#include <cassert>
2#include <iostream> 1#include <iostream>
3#include <map>
4#include <random>
5 2
6#include <Snap.h> 3#include "NameGame.h"
7
8void name_game(int group_size, int cmsize);
9
10constexpr int memory_length = 12;
11std::mt19937 rng(std::random_device{}());
12 4
13int main(int argc, char* argv[]) { 5int main(int argc, char* argv[]) {
14 rng.seed(std::random_device{}());
15 int group_size = 100; 6 int group_size = 100;
7 tp::NameGame namegame(group_size, 0);
16 for (int cmperc = 18; cmperc < 28; cmperc += 2) { 8 for (int cmperc = 18; cmperc < 28; cmperc += 2) {
17 int cmsize = group_size * cmperc / 100; 9 int cmsize = group_size * cmperc / 100;
10 namegame.setCMsize(cmsize);
11 namegame.initMemory();
18 std::cout << "CM " << cmsize << " / " << group_size << std::endl; 12 std::cout << "CM " << cmsize << " / " << group_size << std::endl;
19 name_game(group_size, cmsize); 13 namegame.run(10);
20 }
21 return 0;
22}
23
24using ngData = TPair<TBool, TIntV>;
25
26template<typename TVal, typename TSizeTy>
27auto begin(const TVec<TVal, TSizeTy>& v) -> decltype(v.BegI()) {
28 return v.BegI();
29}
30
31template<typename TVal, typename TSizeTy>
32auto end(const TVec<TVal, TSizeTy>& v) -> decltype(v.EndI()) {
33 return v.EndI();
34}
35
36int best_move(const ngData& d) {
37 const auto& mem = d.Val2;
38 std::map<int, int> votes;
39 for (const auto& v : mem) {
40 if (votes.count(v)) votes[v] += 1;
41 else votes[v] = 1;
42 } 14 }
43 std::map<int, float> fractions;
44 for (auto kv : votes) fractions[kv.first] = kv.second * 1./mem.Len();
45 // Simple majority vote:
46 for (auto kv : fractions)
47 if (kv.second >= 1./mem.Len()) return kv.first;
48 return 0; 15 return 0;
49} 16}
50 17
51void update_memory(ngData& d, int move) {
52 if (!d.Val1) {
53 auto& mem = d.Val2;
54 mem.Ins(0, move);
55 mem.Trunc(memory_length);
56 }
57}
58
59void name_game_round(TNodeNet<ngData>& graph) {
60 std::uniform_int_distribution<> dist(0, graph.GetNodes());
61 // Select random speaker and hearer.
62 auto speaker = graph.GetNI(dist(rng));
63 auto hearer = graph.GetNI(
64 speaker.GetNbrNId(dist(rng) % speaker.GetDeg())
65 );
66
67 // Speaker chooses best strategy.
68 int s_strategy = best_move(speaker.GetDat());
69 std::cout << "speaker (" << speaker.GetId() << " "
70 << std::boolalpha << speaker().Val1
71 << ") chose: " << s_strategy << std::endl;
72
73 // Listener updates memory.
74 update_memory(hearer.GetDat(), s_strategy);
75
76 // Listener chooses best strategy.
77 int h_strategy = best_move(hearer.GetDat());
78 std::cout << "hearer (" << hearer.GetId() << " "
79 << std::boolalpha << hearer().Val1
80 << ") chose: " << h_strategy << std::endl;
81
82 // Speaker updates memory.
83 update_memory(speaker.GetDat(), s_strategy);
84}
85
86void name_game(int group_size, int cmsize) {
87 assert(cmsize > 0 && group_size > 0);
88 assert(cmsize < group_size);
89 TNodeNet<ngData> graph;
90 // Generate complete graph.
91 for (int i = 0; i < group_size; ++i) graph.AddNode(i, {false, {}});
92 for (int i = 0; i < group_size; ++i)
93 for (int j = 0; j < group_size; ++j)
94 if (i != j) graph.AddEdge(i, j);
95 // Set committed minority.
96 std::uniform_int_distribution<> dist(0, group_size - 1);
97 for (int i = 0; i < cmsize;) {
98 int r = dist(rng);
99 if (!graph.GetNDat(r).Val1) {
100 graph.GetNDat(r).Val1 = true;
101 auto& mem = graph.GetNDat(r).Val2;
102 mem.Clr();
103 mem.Add(2);
104 ++i;
105 }
106 }
107 // Establish norm (fully memory of strategy 1)
108 for (int i = 0; i < group_size; ++i) {
109 auto& d = graph.GetNDat(i);
110 if (!d.Val1) while (d.Val2.Len() < memory_length) d.Val2.Add(1);
111 }
112 std::cout << "Nodes: " << graph.GetNodes()
113 << " Edges: " << graph.GetEdges() << std::endl;
114
115 name_game_round(graph);
116 18
117 // Track all plays and plays by non-CM.
118 // Report average plays.
119}