You are viewing the MafiaScum.net Wiki. To play the game, visit the forum.

Code for Telephone Pictionary mods: Difference between revisions

From MafiaWiki
Jump to navigation Jump to search
(Created page with "==Starting the game== The codes written on this page are all C++. If you don't know how to use them and don't have Visual Studio on your computer already, you can download Code:...")
 
 
(11 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{TOCright}}
==Starting the game==
==Starting the game==


The codes written on this page are all C++. If you don't know how to use them and don't have Visual Studio on your computer already, you can download Code::Blocks and MinGW for free. This page is not a tutorial to help you set them up, but feel free to bug [[Ether]] for help if you need it.
The codes written on this page are all C++. If you don't know how to use them and don't have Visual Studio on your computer already, you can download Code::Blocks and MinGW for free. This page is not a tutorial to help you set them up, but feel free to bug [[Ether]] for help if you need it.


Below is a map randomizer you can use at the start of the game. This code is not the only thing you need to run it! In the same folder, you need to also put in two .txt files: a "list.txt", with twenty lines--one for each player, and a "chainnames.txt"--also twenty lines, but noting each chain's name. (Chains don't striiiiictly have to have names, but it's much easier to keep track of. Generally you want one for each letter of the alphabet up through T. Note that the codes on this page all assume 20 players and 13 rounds, and ''will not work'' with less. This is pretty easily fixable, but we'll cross that bridge when we come to it.)
Below is a map randomizer you can use at the start of the game. This code is not the only thing you need to run it! In the same folder, you need to also put in two .txt files: a "list.txt", with twenty lines--one for each player, and a "chainnames.txt"--also twenty lines, but noting each chain's name. (Chains don't striiiiictly have to have names, but it's much easier to keep track of. Generally you want one for each letter of the alphabet up through T. Note that the codes on this page all assume 20 players and 13 rounds, and ''will not work'' with less. Change the constants if you're working with a different number, and make sure your list and chainnames files are at least large enough to accommodate.)


<pre>#include <iostream>
<pre>#include <iostream>
Line 11: Line 13:
#include <stdlib.h>
#include <stdlib.h>
using namespace std;
using namespace std;
const int PLAYERCOUNT = 20;
const int ROUNDS = 13;


struct player{
struct player{
string name;
string name;
bool eachchain[20];
bool eachchain[PLAYERCOUNT];
int thisround;
int thisround;
};
};
Line 21: Line 26:


ifstream allplayers, chainnames;
ifstream allplayers, chainnames;
string chain[20][13];
string chain[PLAYERCOUNT][ROUNDS];
player playerlist[20];
player playerlist[PLAYERCOUNT];
string taxonomy[20];
string taxonomy[PLAYERCOUNT];
ofstream chainmap, skeleton;
ofstream chainmap, skeleton;


Line 34: Line 39:
// Assign players into an array.
// Assign players into an array.
allplayers.open("list.txt");
allplayers.open("list.txt");
while(!allplayers.eof() && counter < 20) {
while(!allplayers.eof() && counter < PLAYERCOUNT) {
getline(allplayers, playerlist[counter].name);
getline(allplayers, playerlist[counter].name);
counter++;
counter++;
Line 42: Line 47:


chainnames.open("chainnames.txt");
chainnames.open("chainnames.txt");
while(!chainnames.eof() && counter < 20) {
while(!chainnames.eof() && counter < PLAYERCOUNT) {
getline(chainnames, taxonomy[counter]);
getline(chainnames, taxonomy[counter]);
counter++;
counter++;
Line 49: Line 54:
counter = 0;
counter = 0;


for(int i = 0; i < 20; i++) {
for(int i = 0; i < PLAYERCOUNT; i++) {
playerlist[i].thisround = -1;
playerlist[i].thisround = -1;
for(int j = 0; j < 20; j++) {
for(int j = 0; j < PLAYERCOUNT; j++) {
playerlist[i].eachchain[j] = false;
playerlist[i].eachchain[j] = false;
}
}
Line 59: Line 64:
srand(time(NULL));
srand(time(NULL));


for(int i = 0; i < 20; i++) {
// For Round 1, everyone just gets whatever spot they had in the playerlist. No randomization needed.
for(int i = 0; i < PLAYERCOUNT; i++) {
playerlist[i].eachchain[i] = true;
playerlist[i].eachchain[i] = true;
chain[i][0] = playerlist[i].name;
chain[i][0] = playerlist[i].name;
Line 65: Line 71:


// Generating player placements for future chains.
// Generating player placements for future chains.
for(int round = 1; round < 13; round++) {
for(int round = 1; round < ROUNDS; round++) {
for(int i = 0; i < 20; i++) playerlist[i].thisround = -1;
for(int i = 0; i < PLAYERCOUNT; i++) playerlist[i].thisround = -1;
counter = 0;
counter = 0;
//godeeper = 0;


for(int chainnum = 0; chainnum < 20; chainnum++) {
for(int chainnum = 0; chainnum < PLAYERCOUNT; chainnum++) {
while(keeprolling == true) {
while(keeprolling == true) {
// Random number from 0-7 the first time, then 0-6, and so on.
// Randomize a player who hasn't been in this chain yet.
whichperson = rand() % (20 - chainnum);
// First, pick a random number based on how many eligible slots there are.
whichperson = rand() % (PLAYERCOUNT - chainnum);


// From 0 to this number, check if each player has been in this round yet.
// There are 20 players in total, so skip anyone who's already been assigned for this round.
for(int i = 0; i <= whichperson; i++) {
// You'll wind up with a random person out of whoever's left.
for(int i = 0; i <= whichperson; i++) {  
if(playerlist[i].thisround > -1) whichperson++;
if(playerlist[i].thisround > -1) whichperson++;
}
}


// If the player hasn't been in this chain yet...
// Make sure the player you picked wasn't already in the chain in an earlier round.
if(playerlist[whichperson].eachchain[chainnum] == false) {
if(playerlist[whichperson].eachchain[chainnum] == false) {
keeprolling = false;
keeprolling = false;
Line 86: Line 93:
if(keeprolling == true) counter++;
if(keeprolling == true) counter++;


// Reroll the current round if that's not possible.
// Sometimes the map backs itself into a corner and nobody left will fit into the remaining slots.
// Keep track of when it's looping too much, and throw the whole column out and start the round over if necessary.
if(counter > 21) {
if(counter > 21) {
for(int i = 0; i < 20; i++) {
for(int i = 0; i < PLAYERCOUNT; i++) {
if(playerlist[i].thisround > -1) {
if(playerlist[i].thisround > -1) {
playerlist[i].eachchain[playerlist[i].thisround] = false;
playerlist[i].eachchain[playerlist[i].thisround] = false;
Line 98: Line 106:
}
}
}
}


chain[chainnum][round] = playerlist[whichperson].name;
chain[chainnum][round] = playerlist[whichperson].name;
Line 106: Line 113:
playerlist[whichperson].thisround = chainnum;
playerlist[whichperson].thisround = chainnum;
keeprolling = true;
keeprolling = true;
//cout << "Chain " << chainnum + 1 << ", round " << round + 1 << ": " << playerlist[whichperson].name;
//cout << " (" << playerlist[whichperson].numchains << " chains)" << endl;
}
}
}
}


chainmap.open("chainmap.txt");
chainmap.open("chainmap.txt");
for(int i = 0; i < 20; i++) {
for(int i = 0; i < PLAYERCOUNT; i++) {
chainmap << "Chain " << (i + 1) << ": ";
chainmap << "Chain " << (i + 1) << ": ";
for(int j = 0; j < 13; j++) {
for(int j = 0; j < ROUNDS; j++) {
chainmap << " - " << chain[i][j];
chainmap << " - " << chain[i][j];
}
}
Line 122: Line 127:


skeleton.open("spreadsheet.csv");
skeleton.open("spreadsheet.csv");
skeleton << "Chain,1,2,3,4,5,6,7,8,9,10,11,12,13" << endl;
skeleton << "Chain";
for(int i = 0; i < 20; i++) {
for (int i = 0; i < ROUNDS; i++) {
        skeleton << "," << i + 1;
    }
    skeleton << endl;
for(int i = 0; i < PLAYERCOUNT; i++) {
skeleton << taxonomy[i];
skeleton << taxonomy[i];
for(int j = 0; j < 13; j++) {
for(int j = 0; j < ROUNDS; j++) {
skeleton << "," << chain[i][j];
skeleton << "," << chain[i][j];
}
}
skeleton << endl << ",,,,,,,,,,,,," << endl;
skeleton << endl;
for (int j = 0; j < ROUNDS; j++) {
            skeleton << ",";
}
skeleton << endl;
}
}


Line 134: Line 147:
}</pre>
}</pre>


When you run this for the first time, you should find two new files the next time you check the folder. (These will both be overwritten every time you generate a new chain map, so rename them or put them in a new folder if you want to keep them.) One will be a .txt file with a quick summary of the map. You don't need this, but at least it looks nice. The other will be a .csv spreadsheet with all the same information, and also gratuitous commas.  
When you run this for the first time, you should find two new files the next time you check the folder. (These will both be overwritten every time you generate a new chain map, so rename them or put them in a new folder if you want to keep them.) One will be a .txt file with a quick summary of the map. You don't need this, but it might help you keep track of things. The other will be a .csv spreadsheet with all the same information, and also gratuitous commas.  


With your spreadsheet skeleton in hand, you can go to Google Drive and make a proper spreadsheet out of it. Just go to File - Import for that. The result will be ugly. .csv files don't care about word wrap or cell sizes or background colors or anything like that. Feel free to fix it up to your taste, and fill in the empty cells as people get their assignments in. New rows and columns ''will'' affect the wiki formatter at the end of the game, so don't add them on a whim.
With your spreadsheet skeleton in hand, you can go to Google Drive and make a proper spreadsheet out of it. Just go to File - Import for that. The result will be ugly. .csv files don't care about word wrap or cell sizes or background colors or anything like that. Feel free to fix it up to your taste, and fill in the empty cells as people get their assignments in. New rows and columns ''will'' affect the wiki formatter at the end of the game, so don't add them on a whim.
Line 144: Line 157:
==Finishing up==
==Finishing up==


When you want the wiki formatting for the index or a subpage, go to the spreadsheet and choose File - Download as - .CSV. Put that into the same folder as this code and run it.
When you want the wiki formatting for the index or a subpage, go to the spreadsheet and choose File - Download as - .CSV. Rename it to spreadsheet.csv. Put that into the same folder as this code and run it.


<pre>#include <iostream>
<pre>#include <iostream>
Line 152: Line 165:


using namespace std;
using namespace std;
const int PLAYERCOUNT = 20;
const int ROUNDS = 13;


struct cell {
struct cell {
     string player;
     string player;
     string entry;
     string entry;
    //bool isPicture;
};
};


Line 175: Line 189:


// This "turn all double-quotes in the line into pilcrows and then turn individual cells back" plan
// This "turn all double-quotes in the line into pilcrows and then turn individual cells back" plan
// is pretty unprofessional.
// is pretty unprofessional. Good thing I'm not a professional!
// Good thing I'm not a professional!
string stopcrowing(string line) {
string stopcrowing(string line) {
     int i;
     int i;
Line 218: Line 231:
     int i;
     int i;
     i = url.find_last_of(".");
     i = url.find_last_of(".");
    if(i == -1) return "..";
     return url.substr(i, url.length());
     return url.substr(i, url.length());
}
}
Line 233: Line 247:


int main() {
int main() {
     int subcheck[13];
     int subcheck[ROUNDS];
     for(int i = 0; i < 13; i++) subcheck[i] = 0;
     for(int i = 0; i < ROUNDS; i++) subcheck[i] = 0;


     string chainname[20];
     string chainname[PLAYERCOUNT];
     cell chainmap[20][13];
     cell chainmap[PLAYERCOUNT][ROUNDS];
     string currentline, placeholder;
     string currentline, placeholder;
     ifstream spreadsheet;
     ifstream spreadsheet;
Line 264: Line 278:
     }
     }


     for(int i = 0; i < 20; i++) {
     for(int i = 0; i < PLAYERCOUNT; i++) {
         getline(spreadsheet, currentline);
         getline(spreadsheet, currentline);
         currentline = pilcrow(currentline);
         currentline = pilcrow(currentline);
         separate(currentline, chainname[i]);
         separate(currentline, chainname[i]);
         for(int j = 0; j < 13; j++) {
         for(int j = 0; j < ROUNDS; j++) {
             for(int r = 0; r <= subcheck[j]; r++) {
             for(int r = 0; r <= subcheck[j]; r++) {
                 separate(currentline, placeholder);
                 separate(currentline, placeholder);
Line 277: Line 291:
         currentline = pilcrow(currentline);
         currentline = pilcrow(currentline);
         separate(currentline, placeholder);
         separate(currentline, placeholder);
         for(int j = 0; j < 13; j++) {
         for(int j = 0; j < ROUNDS; j++) {
             for(int r = 0; r <= subcheck[j]; r++) {
             for(int r = 0; r <= subcheck[j]; r++) {
                 separate(currentline, placeholder);
                 separate(currentline, placeholder);
Line 290: Line 304:
index_format.open("index_format.txt");
index_format.open("index_format.txt");
index_format << "[insert a description here]" << endl << endl;
index_format << "[insert a description here]" << endl << endl;
index_format << "====What is Telephone Pictionary?====" << endl;
index_format << "It\'s sort of like [[Eat Poop You Cat|EPYC]], but presumably with less poop-eating." << endl << endl;
index_format << "==The Player List==" << endl << endl << "{{multicol}}" << endl;
index_format << "==The Player List==" << endl << endl << "{{multicol}}" << endl;
for(int i = 1; i < 11; i++) {
for(int i = 1; i < (PLAYERCOUNT / 2) + 1; i++) {
index_format << "\'\'\'" << i << "\'\'\'. {{U|" << despaced(chainmap[i-1][0].player) << "}}<br />" << endl;
index_format << "\'\'\'" << i << "\'\'\'. {{U|" << despaced(chainmap[i-1][0].player);
index_format << "}}<br />" << endl;
}
}
index_format << "{{multicol-break}}" << endl;
index_format << "{{multicol-break}}" << endl;
for(int i = 11; i < 21; i++) {
for(int i = (PLAYERCOUNT / 2) + 1; i < PLAYERCOUNT + 1; i++) {
index_format << "\'\'\'" << i << ".\'\'\' {{U|" << despaced(chainmap[i-1][0].player) << "}}<br />" << endl;
index_format << "\'\'\'" << i << ".\'\'\' {{U|" << despaced(chainmap[i-1][0].player);
index_format << "}}<br />" << endl;
}
}
index_format << "{{multicol-end}}" << endl << endl;
index_format << "{{multicol-end}}" << endl << endl;
index_format << "==The Chains==" << endl  << endl << endl;
index_format << "==The Chains==" << endl  << endl << endl;
index_format << "{| style=\"border:0px !important;border-spacing:15px;\"" << endl;
index_format << "{| style=\"border:0px !important;border-spacing:15px;\"" << endl;
for(int i = 0; i < 20; i++) {
for(int i = 0; i < PLAYERCOUNT; i++) {
index_format << "| \'\'\'<span style=\"font-size:120%;\">[[/Chain ";
index_format << "| \'\'\'<span style=\"font-size:120%;\">[[/Chain ";
index_format << (char)(i+65) << "|" << (char)(i+65) << ". ";
index_format << (char)(i+65) << "|" << (char)(i+65) << ". ";
index_format << chainmap[i][0].player << "\'s Chain]]</span>\'\'\'" << endl << "----" << endl;
index_format << chainmap[i][0].player << "\'s Chain]]</span>\'\'\'" << endl << "----" << endl;
for(int j = 0; j < 13; j++) {
for(int j = 0; j < ROUNDS; j++) {
index_format << "*{{U|" << despaced(chainmap[i][j].player) << "}} ";
index_format << "*{{U|" << despaced(chainmap[i][j].player) << "}} ";
if(j % 2 == 0) index_format << "(phrase)" << endl;
if(j % 2 == 0) index_format << "(phrase)" << endl;
else index_format << "(picture)" << endl;
else index_format << "(picture)" << endl;
}
}
if((i+1) % 5 == 0 && i != 19) index_format << "|-" << endl;
if((i+1) % 5 == 0 && i != PLAYERCOUNT) index_format << "|-" << endl;
}
}
index_format << "|}" << endl << endl << "[[Category:Mish Mash]]" << endl;
index_format << "|}" << endl << endl << "[[Category:Mish Mash]]" << endl;
Line 324: Line 338:
cin >> prefix;
cin >> prefix;
subpage_format.open("subpage_format.txt");
subpage_format.open("subpage_format.txt");
for(int i = 0; i < 20; i++) {
for(int i = 0; i < PLAYERCOUNT; i++) {
subpage_format << "<br />";
subpage_format << "<br />";
if(i > 0) {
if(i > 0) {
Line 331: Line 345:
subpage_format << "|« Previous]]</div>";
subpage_format << "|« Previous]]</div>";
}
}
if(i != 19) {
if(i != PLAYERCOUNT - 1) {
subpage_format << "<div style=\"float:right;font-size:150%;font-weight:bold;\">";
subpage_format << "<div style=\"float:right;font-size:150%;font-weight:bold;\">";
subpage_format << "[[Telephone Pictionary: " << gamename << "/Chain " << (char)(i+66);
subpage_format << "[[Telephone Pictionary: " << gamename << "/Chain " << (char)(i+66);
Line 337: Line 351:
}
}
subpage_format << "<br /><br />" << endl << endl;
subpage_format << "<br /><br />" << endl << endl;
subpage_format << "==Chain " << (char)(i+65) << ": " << chainmap[i][0].player << "\'s Chain==" << endl << endl;
subpage_format << "==Chain " << (char)(i+65) << ": " << chainmap[i][0].player;
for(int j = 0; j < 13; j++) {
subpage_format << "\'s Chain==" << endl << endl;
for(int j = 0; j < ROUNDS; j++) {
if(j % 2 == 0) {
if(j % 2 == 0) {
subpage_format << "<span style=\"font-size:130%;color:#00bf80;\">\'\'\'{{U|";
subpage_format << "<span style=\"font-size:130%;color:#00bf80;\">\'\'\'{{U|";
Line 358: Line 373:
subpage_format << "|« Previous]]</div>";
subpage_format << "|« Previous]]</div>";
}
}
if(i != 19) {
if(i != PLAYERCOUNT - 1) {
subpage_format << "<div style=\"float:right;font-size:150%;font-weight:bold;\">";
subpage_format << "<div style=\"float:right;font-size:150%;font-weight:bold;\">";
subpage_format << "[[Telephone Pictionary: " << gamename << "/Chain " << (char)(i+66);
subpage_format << "[[Telephone Pictionary: " << gamename << "/Chain " << (char)(i+66);
subpage_format << "|Next »]]</div><br /><br />";
subpage_format << "|Next »]]</div>";
}
}
subpage_format << endl << endl << endl << "[[Category:Telephone Pictionary]]" << endl << endl;
subpage_format << "<br /><br />[[Category:Telephone Pictionary]]" << endl << endl << endl;
subpage_format << "---" << endl << endl;
}
}
subpage_format.close();
subpage_format.close();


 
return 0;
    return 0;
}</pre>
}
</pre>


That'll give you two .txt files, again in the same folder: an index_format and a subpage_format (the latter containing the code for all 20 subpages). It won't be perfectly complete unless the spreadsheet is, but you can redownload the .csv file and rerun this as many times as you want, and even if you don't, the rest is pretty easy to fill in.
That'll give you two .txt files, again in the same folder: an index_format and a subpage_format (the latter containing the code for all 20 subpages). It won't be perfectly complete unless the spreadsheet is, but you can redownload the .csv file and rerun this as many times as you want, and even if you don't, the rest is pretty easy to fill in.
Line 376: Line 390:
Note that those text files don't accommodate for things like BBCode, HTML or line breaks in people's sentences. But that's straightforward to fix by hand.<br/><br/>
Note that those text files don't accommodate for things like BBCode, HTML or line breaks in people's sentences. But that's straightforward to fix by hand.<br/><br/>


== A Noobish Windows User's Guide to Telephone Pictionary Modding ==
Hello, this advice is written by a new Telephone Pictionary mode based on personal experience.
=== Before Starting ===
1. To use Ether's code without significant edits, you will need to have a minimum of 20 players but can easily increase this number. Decide when you make your signup thread how many players you are willing to accommodate and how many rounds each chain will last.
2. If wanting to borrow any rules/formatting from a previous signup thread, feel free to refer to [https://forum.mafiascum.net/viewtopic.php?t=91240 Telephone Pictionary: Sumbarine Sheen] or [https://forum.mafiascum.net/viewtopic.php?f=10&t=86893 Telephone Pictionary: Waggingly Good Tales] as examples.
3. Download Visual Studio from Microsoft's website. I downloaded the Community 2022 version.
4. Decide what to name your chains.
=== After Signups Close - Implementing the Code ===
If you get stuck on any of these steps, you probably will want to try to reach out to a prior Telephone Pictionary mod who has a coding background.
1. In Visual Studio, create a Project. Choose Console App as the type. Check the "Place solution and project in the same directory" box.
2. In Notepad, create a .txt file (list.txt). This should have the names of your players, one per line. Save it into the same folder as your project.
3. In Notepad, create a .txt file (chainnames.txt). This should have the names of your chains, one per line. Save it into the same folder as your project.
4. Replace all the text in the Console App .cpp file with Ether's "Starting the game" code above.
5. If you are changing the number of players or the chain length, replace the number at this part of the code with a different number:
<pre>
const int PLAYERCOUNT = 20;
const int ROUNDS = 13;</pre>
6. Click the green triangle for Local Windows Debugger at the top.
7. Check the project folder and ideally you will see two new files: a spreadsheet, and a chainmap.txt file.
=== Staying Organized During the Game ===
You will likely want to edit the spreadsheet that the coding spits out for convenience purposes. I used the blank rows between chains to paste the sentence or image URL beneath the name of the person that submitted it. I made an additional tab for tracking player status (their communication style preference, their current task, and when the current task was sent to them.) I also made an additional tab for a visualization of chain status to be able to provide updates to the thread.
=== Uploading new chains to the wiki ===
I never actually got the "Finishing Up" code above to work, but feel free to give the instructions above a go!
Here is a 5 minute video tutorial for an alternative workflow: https://www.youtube.com/watch?v=TORVICvYEWE


[[Category:Mish Mash]][[Category:Telephone Pictionary]]
[[Category:Mish Mash]][[Category:Telephone Pictionary]]

Latest revision as of 16:11, 14 January 2024


Starting the game

The codes written on this page are all C++. If you don't know how to use them and don't have Visual Studio on your computer already, you can download Code::Blocks and MinGW for free. This page is not a tutorial to help you set them up, but feel free to bug Ether for help if you need it.

Below is a map randomizer you can use at the start of the game. This code is not the only thing you need to run it! In the same folder, you need to also put in two .txt files: a "list.txt", with twenty lines--one for each player, and a "chainnames.txt"--also twenty lines, but noting each chain's name. (Chains don't striiiiictly have to have names, but it's much easier to keep track of. Generally you want one for each letter of the alphabet up through T. Note that the codes on this page all assume 20 players and 13 rounds, and will not work with less. Change the constants if you're working with a different number, and make sure your list and chainnames files are at least large enough to accommodate.)

#include <iostream>
#include <fstream>
#include <string>
#include <time.h>
#include <stdlib.h>
using namespace std;

const int PLAYERCOUNT = 20;
const int ROUNDS = 13;

struct player{
	string name;
	bool eachchain[PLAYERCOUNT];
	int thisround;
};

int main() {

	ifstream allplayers, chainnames;
	string chain[PLAYERCOUNT][ROUNDS];
	player playerlist[PLAYERCOUNT];
	string taxonomy[PLAYERCOUNT];
	ofstream chainmap, skeleton;

	int whichperson, counter;
	bool keeprolling;

	counter = 0;
	keeprolling = true;

	// Assign players into an array.
	allplayers.open("list.txt");
	while(!allplayers.eof() && counter < PLAYERCOUNT) {
		getline(allplayers, playerlist[counter].name);
		counter++;
	}
	allplayers.close();
	counter = 0;

	chainnames.open("chainnames.txt");
	while(!chainnames.eof() && counter < PLAYERCOUNT) {
		getline(chainnames, taxonomy[counter]);
		counter++;
	}
	chainnames.close();
	counter = 0;

	for(int i = 0; i < PLAYERCOUNT; i++) {
		playerlist[i].thisround = -1;
		for(int j = 0; j < PLAYERCOUNT; j++) {
			playerlist[i].eachchain[j] = false;
		}
	}


	srand(time(NULL));

	// For Round 1, everyone just gets whatever spot they had in the playerlist. No randomization needed.
	for(int i = 0; i < PLAYERCOUNT; i++) {
		playerlist[i].eachchain[i] = true;
		chain[i][0] = playerlist[i].name;
	}

	// Generating player placements for future chains.
	for(int round = 1; round < ROUNDS; round++) {
		for(int i = 0; i < PLAYERCOUNT; i++) playerlist[i].thisround = -1;
		counter = 0;

		for(int chainnum = 0; chainnum < PLAYERCOUNT; chainnum++) {
			while(keeprolling == true) {
					// Randomize a player who hasn't been in this chain yet.
					// First, pick a random number based on how many eligible slots there are.
					whichperson = rand() % (PLAYERCOUNT - chainnum);

					// There are 20 players in total, so skip anyone who's already been assigned for this round.
					// You'll wind up with a random person out of whoever's left.
					for(int i = 0; i <= whichperson; i++) { 
						if(playerlist[i].thisround > -1) whichperson++;
					}

				// Make sure the player you picked wasn't already in the chain in an earlier round.
				if(playerlist[whichperson].eachchain[chainnum] == false) {
					keeprolling = false;
				}
				if(keeprolling == true) counter++;

				// Sometimes the map backs itself into a corner and nobody left will fit into the remaining slots.
				// Keep track of when it's looping too much, and throw the whole column out and start the round over if necessary.
				if(counter > 21) {
					for(int i = 0; i < PLAYERCOUNT; i++) {
						if(playerlist[i].thisround > -1) {
							playerlist[i].eachchain[playerlist[i].thisround] = false;
						}
						playerlist[i].thisround = -1;
					}
				counter = 0;
				chainnum = 0;
				}
			}

			chain[chainnum][round] = playerlist[whichperson].name;

			// Updates.
			playerlist[whichperson].eachchain[chainnum] = true;
			playerlist[whichperson].thisround = chainnum;
			keeprolling = true;
		}
	}

	chainmap.open("chainmap.txt");
	for(int i = 0; i < PLAYERCOUNT; i++) {
		chainmap << "Chain " << (i + 1) << ": ";
		for(int j = 0; j < ROUNDS; j++) {
			chainmap << " - " << chain[i][j];
		}
		chainmap << endl;
	}
	chainmap.close();

	skeleton.open("spreadsheet.csv");
	skeleton << "Chain";
	for (int i = 0; i < ROUNDS; i++) {
        skeleton << "," << i + 1;
    }
    skeleton << endl;
	for(int i = 0; i < PLAYERCOUNT; i++) {
		skeleton << taxonomy[i];
		for(int j = 0; j < ROUNDS; j++) {
			skeleton << "," << chain[i][j];
		}
		skeleton << endl;
		for (int j = 0; j < ROUNDS; j++) {
            skeleton << ",";
		}
		skeleton << endl;
	}

	return 0;
}

When you run this for the first time, you should find two new files the next time you check the folder. (These will both be overwritten every time you generate a new chain map, so rename them or put them in a new folder if you want to keep them.) One will be a .txt file with a quick summary of the map. You don't need this, but it might help you keep track of things. The other will be a .csv spreadsheet with all the same information, and also gratuitous commas.

With your spreadsheet skeleton in hand, you can go to Google Drive and make a proper spreadsheet out of it. Just go to File - Import for that. The result will be ugly. .csv files don't care about word wrap or cell sizes or background colors or anything like that. Feel free to fix it up to your taste, and fill in the empty cells as people get their assignments in. New rows and columns will affect the wiki formatter at the end of the game, so don't add them on a whim.

There is one exception to this: you do need to accommodate for people taking on sub duties. When someone steps up, add another column to the right of the round where a replacee missed the deadline. Fill in only the replacement's name and submission. Do not give the column a name. It's ugly, but you can hide columns in Google Spreadsheet if you don't need to look at them anymore.

Also, pilcrows (¶) will mess up the formatting for the wikification process. Professional, this code is not. Anyway, it's not like anyone would have ever had any reason to use that symbol if I hadn't made this note, so just smack them if they try.

Finishing up

When you want the wiki formatting for the index or a subpage, go to the spreadsheet and choose File - Download as - .CSV. Rename it to spreadsheet.csv. Put that into the same folder as this code and run it.

#include <iostream>
#include <string>
#include <fstream>
#include <stdlib.h>

using namespace std;
const int PLAYERCOUNT = 20;
const int ROUNDS = 13;

struct cell {
    string player;
    string entry;
};

// I'm assuming people don't use pilcrows.
// If they start using them because this code is released,
// you may hit them.
string pilcrow(string line) {
    int i;
    bool done;
    done = false;
    do {
        i = line.find("\"\"");
        if(i == -1) done = true;
        else line.replace(i, 2, "¶");
    } while(!done);
    return line;
}

// This "turn all double-quotes in the line into pilcrows and then turn individual cells back" plan
// is pretty unprofessional. Good thing I'm not a professional!
string stopcrowing(string line) {
    int i;
    bool done;
    done = false;
    do {
        i = line.find("¶");
        if(i == -1) done = true;
        else line.replace(i, 1, "\"");
    } while(!done);
    return line;
}

// Split the line into two components:
// the first cell that wasn't already split off, and the rest of the line.
void separate(string& line, string& first) {
    int i;
    if(line.substr(0, 1) == "\"" || (line.substr(0, 1) == "¶" && line.substr(1, 1) == "\"")) {
        i = line.find("\",");
        if(i == -1) {
            first = line.substr(1, line.length() - 2);
            line = "";
            return;
        }
        first = line.substr(1, i - 1);
        line = line.substr(i+2, line.length());
    }
    else {
        i = line.find(",");
        if(i == -1) {
            first = line.substr(0, line.length());
            line = "";
            return;
        }
        first = line.substr(0, i);
        line = line.substr(i+1, line.length());
    }
}

string extension(string url) {
    int i;
    i = url.find_last_of(".");
    if(i == -1) return "..";
    return url.substr(i, url.length());
}

string despaced(string original) {
	int i;
	do {
		i = original.find(" ");
		if(i == -1) return original;
		original.replace(i, 1, " ");
	} while(i != -1);

	return original;
}

int main() {
    int subcheck[ROUNDS];
    for(int i = 0; i < ROUNDS; i++) subcheck[i] = 0;

    string chainname[PLAYERCOUNT];
    cell chainmap[PLAYERCOUNT][ROUNDS];
    string currentline, placeholder;
    ifstream spreadsheet;
    ofstream index_format, subpage_format, test;
    int k;
    k = 0;

    spreadsheet.open("spreadsheet.csv");
    if(!spreadsheet.is_open()) spreadsheet.open("spreadsheet.txt");
    if(!spreadsheet.is_open()) {
            cout << "That didn't open. Please add a file entitled \"spreadsheet.csv\" to this folder and try again.";
            cout << " (This should be the renamed copy of the .csv file you downloaded from Google Spreadsheets.)";
            return -1;
    }
    getline(spreadsheet, currentline);

    //Discard the "Chain" and "1" cells; those will always be in the same columns.
    separate(currentline, placeholder);
    separate(currentline, placeholder);

    // Track whether any given round has replacements to watch out for.
    while(currentline != ""){
        separate(currentline, placeholder);
        if(placeholder == "") subcheck[k]++;
        else k++;
    }

    for(int i = 0; i < PLAYERCOUNT; i++) {
        getline(spreadsheet, currentline);
        currentline = pilcrow(currentline);
        separate(currentline, chainname[i]);
        for(int j = 0; j < ROUNDS; j++) {
            for(int r = 0; r <= subcheck[j]; r++) {
                separate(currentline, placeholder);
                if(placeholder != "" || r == 0) chainmap[i][j].player = stopcrowing(placeholder);
            }
        }
        getline(spreadsheet, currentline);
        currentline = pilcrow(currentline);
        separate(currentline, placeholder);
        for(int j = 0; j < ROUNDS; j++) {
            for(int r = 0; r <= subcheck[j]; r++) {
                separate(currentline, placeholder);
                if(placeholder != "") chainmap[i][j].entry = stopcrowing(placeholder);
            }
        }
    }

    spreadsheet.close();

    //Index formatting. This accounts for any replacements that already exist.
	index_format.open("index_format.txt");
	index_format << "[insert a description here]" << endl << endl;
	index_format << "==The Player List==" << endl << endl << "{{multicol}}" << endl;
	for(int i = 1; i < (PLAYERCOUNT / 2) + 1; i++) {
		index_format << "\'\'\'" << i << "\'\'\'. {{U|" << despaced(chainmap[i-1][0].player);
		index_format << "}}<br />" << endl;
	}
	index_format << "{{multicol-break}}" << endl;
	for(int i = (PLAYERCOUNT / 2) + 1; i < PLAYERCOUNT + 1; i++) {
		index_format << "\'\'\'" << i << ".\'\'\' {{U|" << despaced(chainmap[i-1][0].player);
		index_format << "}}<br />" << endl;
	}
	index_format << "{{multicol-end}}" << endl << endl;
	index_format << "==The Chains==" << endl  << endl << endl;
	index_format << "{| style=\"border:0px !important;border-spacing:15px;\"" << endl;
	for(int i = 0; i < PLAYERCOUNT; i++) {
		index_format << "| \'\'\'<span style=\"font-size:120%;\">[[/Chain ";
		index_format << (char)(i+65) << "|" << (char)(i+65) << ". ";
		index_format << chainmap[i][0].player << "\'s Chain]]</span>\'\'\'" << endl << "----" << endl;
		for(int j = 0; j < ROUNDS; j++) {
			index_format << "*{{U|" << despaced(chainmap[i][j].player) << "}} ";
			if(j % 2 == 0) index_format << "(phrase)" << endl;
			else index_format << "(picture)" << endl;
		}
		if((i+1) % 5 == 0 && i != PLAYERCOUNT) index_format << "|-" << endl;
	}
	index_format << "|}" << endl << endl << "[[Category:Mish Mash]]" << endl;
	index_format.close();

	// Now for the subpages!
	string gamename, prefix;
	cout << "What\'s the game\'s name going to be\? (An example is \"The Ramen Years\".)" << endl;
	getline(cin, gamename);
	cout << "And the prefix for image files? (Examples from earlier games are Telcat, Telram and Telekoop.)" << endl;
	cin >> prefix;
	subpage_format.open("subpage_format.txt");
	for(int i = 0; i < PLAYERCOUNT; i++) {
		subpage_format << "<br />";
		if(i > 0) {
			subpage_format << "<div style=\"float:left;font-size:150%;font-weight:bold;\">";
			subpage_format << "[[Telephone Pictionary: " << gamename << "/Chain " << (char)(i+64);
			subpage_format << "|« Previous]]</div>";
		}
		if(i != PLAYERCOUNT - 1) {
			subpage_format << "<div style=\"float:right;font-size:150%;font-weight:bold;\">";
			subpage_format << "[[Telephone Pictionary: " << gamename << "/Chain " << (char)(i+66);
			subpage_format << "|Next »]]</div>";
		}
		subpage_format << "<br /><br />" << endl << endl;
		subpage_format << "==Chain " << (char)(i+65) << ": " << chainmap[i][0].player;
		subpage_format << "\'s Chain==" << endl << endl;
		for(int j = 0; j < ROUNDS; j++) {
			if(j % 2 == 0) {
				subpage_format << "<span style=\"font-size:130%;color:#00bf80;\">\'\'\'{{U|";
				subpage_format << despaced(chainmap[i][j].player) << "}} - Phrase \'\'\'</span>" << endl;
				subpage_format << ":<span style=\"font-size:170%;color:#434343;\">";
				subpage_format << chainmap[i][j].entry << "</span>" << endl << endl << endl;
			}
			else {
				subpage_format << "<span style=\"font-size:130%;color:#ff0040;\">\'\'\'{{U|";
				subpage_format << despaced(chainmap[i][j].player) << "}} - Picture\'\'\'</span>" << endl;
				subpage_format << ":[[Image:" << prefix << "_" << (char)(i+65) << (j+1)/2;
				subpage_format << extension(chainmap[i][j].entry) << "]]" << endl << endl << endl;;
			}
		}
		subpage_format << "<br />";
		if(i > 0) {
			subpage_format << "<div style=\"float:left;font-size:150%;font-weight:bold;\">";
			subpage_format << "[[Telephone Pictionary: " << gamename << "/Chain " << (char)(i+64);
			subpage_format << "|« Previous]]</div>";
		}
		if(i != PLAYERCOUNT - 1) {
			subpage_format << "<div style=\"float:right;font-size:150%;font-weight:bold;\">";
			subpage_format << "[[Telephone Pictionary: " << gamename << "/Chain " << (char)(i+66);
			subpage_format << "|Next »]]</div>";
		}
		subpage_format << "<br /><br />[[Category:Telephone Pictionary]]" << endl << endl << endl;
		subpage_format << "---" << endl << endl;
	}
	subpage_format.close();

	return 0;
}

That'll give you two .txt files, again in the same folder: an index_format and a subpage_format (the latter containing the code for all 20 subpages). It won't be perfectly complete unless the spreadsheet is, but you can redownload the .csv file and rerun this as many times as you want, and even if you don't, the rest is pretty easy to fill in.

Note that those text files don't accommodate for things like BBCode, HTML or line breaks in people's sentences. But that's straightforward to fix by hand.

A Noobish Windows User's Guide to Telephone Pictionary Modding

Hello, this advice is written by a new Telephone Pictionary mode based on personal experience.

Before Starting

1. To use Ether's code without significant edits, you will need to have a minimum of 20 players but can easily increase this number. Decide when you make your signup thread how many players you are willing to accommodate and how many rounds each chain will last.

2. If wanting to borrow any rules/formatting from a previous signup thread, feel free to refer to Telephone Pictionary: Sumbarine Sheen or Telephone Pictionary: Waggingly Good Tales as examples.

3. Download Visual Studio from Microsoft's website. I downloaded the Community 2022 version.

4. Decide what to name your chains.

After Signups Close - Implementing the Code

If you get stuck on any of these steps, you probably will want to try to reach out to a prior Telephone Pictionary mod who has a coding background.

1. In Visual Studio, create a Project. Choose Console App as the type. Check the "Place solution and project in the same directory" box.

2. In Notepad, create a .txt file (list.txt). This should have the names of your players, one per line. Save it into the same folder as your project.

3. In Notepad, create a .txt file (chainnames.txt). This should have the names of your chains, one per line. Save it into the same folder as your project.

4. Replace all the text in the Console App .cpp file with Ether's "Starting the game" code above.

5. If you are changing the number of players or the chain length, replace the number at this part of the code with a different number:

const int PLAYERCOUNT = 20;

const int ROUNDS = 13;

6. Click the green triangle for Local Windows Debugger at the top.

7. Check the project folder and ideally you will see two new files: a spreadsheet, and a chainmap.txt file.

Staying Organized During the Game

You will likely want to edit the spreadsheet that the coding spits out for convenience purposes. I used the blank rows between chains to paste the sentence or image URL beneath the name of the person that submitted it. I made an additional tab for tracking player status (their communication style preference, their current task, and when the current task was sent to them.) I also made an additional tab for a visualization of chain status to be able to provide updates to the thread.

Uploading new chains to the wiki

I never actually got the "Finishing Up" code above to work, but feel free to give the instructions above a go!

Here is a 5 minute video tutorial for an alternative workflow: https://www.youtube.com/watch?v=TORVICvYEWE