/*
Module : Country
Purpose: Operations to do with the 'country' array.
Version: 0.05
Date : 5th June 2008
Started: 11th July 2005
Author : Rick Murray
http://www.heyrick.co.uk/eurovision/
*/
#include "euroscore.h"
struct countrydef *country = NULL;
unsigned long countrycount = 0; // how many we have right now
unsigned long countrymax = 0; // how many we can have before reallocation required
int scorelist[] = {1, 2, 3, 4, 5, 6, 7, 8, 10, 12};
void country_extendarray(void);
void country_tidyup(void);
void country_process(void);
void country_outputeach(void);
void country_gentable(int which);
void country_votedforme(int which);
char *country_alias(int which, int title);
char *country_matchtoalias(char *ctry);
void country_extendarray(void)
{
/* Creates/extends the 'country' array for additional entries */
// first time? set tidyup handler
if (countrymax == 0)
atexit(country_tidyup);
countrymax += 10; // extend array by ten entries at a time...
country = memtrace_namedrealloc(country, (countrymax * sizeof(struct countrydef)), __func__);
if (country == NULL)
{
fprintf(stderr, "Unable to (re)allocate memory for country array.\n\n");
exit(EXIT_FAILURE);
}
return;
}
void country_tidyup(void)
{
/* Delete array on exit... */
if (country == NULL)
{
fprintf(stderr, "Country array not allocated or already freed!\n");
return;
}
memtrace_free(country);
country = NULL;
return;
}
void country_process(void)
{
/* Works out, from the scores, what 'position' the country would rank */
struct scoredef
{
int offset;
int score;
signed int position;
};
struct scoredef *scores = NULL;
int recurse = 0;
int value = 0;
int loop = 0;
int count = 0;
int sortdone = FALSE;
// Count how many actual scores we have
for (recurse = 0; recurse < countrycount; recurse++)
if (country[recurse].score >= 0)
count++;
// Allocate this much in our array
scores = memtrace_calloc((count + 2), sizeof(struct scoredef));
if (scores == NULL)
{
fprintf(stderr, "Unable to allocate memory for %d score definitions.\n\n", count);
exit(EXIT_FAILURE);
}
// Copy across into the score array
count = 0;
for (recurse = 0; recurse < countrycount; recurse++)
{
if (country[recurse].score != -1)
{
count++; // pre-increment, as array 0 is sort buffer and we leave with 'correct' count
scores[count].offset = recurse;
scores[count].score = country[recurse].score;
scores[count].position = 0;
}
}
// Basic Boring Bubble Sort
// This is a terrible sort, but it gets the job done and ... I've had a few glasses
// of rosé wine while enjoying a good film on TV and frankly a crappy sorting routine
// with no imagination is about right for my current state of mind.
// [ I betcha you're glad you're reading this code now, huh? :-) ]
//
// The only thing to note about the sort routine is that it works BACKWARDS:
// highest entry first, because the song with the most points is #1!
do
{
sortdone = TRUE;
for (recurse = count; recurse > 0; recurse--)
{
if ( scores[recurse].score < scores[(recurse + 1)].score )
{
scores[0] = scores[(recurse + 1)];
scores[(recurse + 1)] = scores[recurse];
scores[recurse] = scores[0];
sortdone = FALSE;
}
}
} while ( sortdone == FALSE );
// Put the positions into the array (for dupe checking)
for (recurse = 1; recurse <= count; recurse++)
scores[recurse].position = recurse;
// Now we must dupe-check
for (recurse = 1; recurse <= count; recurse++)
{
int isadupe = FALSE;
// get a score
value = scores[recurse].score;
// check against every FOLLOWING entry
for (loop = (recurse + 1); loop <= count; loop++)
{
// a match?
if (scores[loop].score == value)
{
isadupe = TRUE; // so we know to dupify the original
scores[loop].position = (0 - scores[recurse].position); // -ve
}
}
// original needs fixing?
if (isadupe == TRUE)
scores[recurse].position = (0 - scores[recurse].position);
}
// now copy the positional information BACK to the main array
for (recurse = 1; recurse <= count; recurse++)
country[scores[recurse].offset].ranking = scores[recurse].position;
// release memory, we're done!
memtrace_free(scores);
return;
}
void country_outputeach(void)
{
/* Sort out what to output for each country */
int loop = 0;
for (loop = 0; loop < countrycount; loop++)
country_gentable(loop);
// I could have made it a two-line function:
// for (int loop = 0; loop < countrycount; loop++)
// country_gentable(loop);
// but OpenWatcom doesn't support this extension to the C language
// (the definition of a variable at first call - "for (INT loop = [etc]".
// Oh well.
// Acorn (Norcroft) C/C++ wins again! :-)
return;
}
void country_gentable(int which)
{
/* Generates the HTML for the table (for each country) */
int sloop = 0;
double pop = 0;
int tblwid = 0;
int trswid = 0;
struct imgsizedef *imgsize = NULL;
fprintf(out, "<a name=\"#%s\"></a>\r\n", country[which].name);
fprintf(out, "<table border=\"0\" width=\"100%%\">\r\n <tr>\r\n");
fprintf(out, " <td colspan=\"3\"><big><big><big><b>%s</b></big></big> ", \
country_alias(which, 1));
if (country[which].score != -1)
{
int myplace = 0;
// we got a score!
fprintf(out, "<font color=\"#2F2F4F\">[%d points", country[which].score);
// the position
if (country[which].ranking < 0)
{
// JOINT ranking!
myplace = abs(country[which].ranking);
fprintf(out, ", joint %d", myplace);
}
else
{
myplace = country[which].ranking;
fprintf(out, ", %d", myplace);
}
// Append 'st', 'nd', 'rd', or 'th'. Allows up to 50 songs (God help Wogan in THAT case!)
switch (myplace)
{
case 1 : /* Everything XXst */
case 21 :
case 31 : /* fall-throughs are intentional! */
case 41 : fprintf(out, "<sup>st</sup>");
break;
case 2 : /* Everything XXnd */
case 22 :
case 32 : /* fall-throughs are intentional! */
case 42 : fprintf(out, "<sup>nd</sup>");
break;
case 3 : /* Everything XXrd */
case 23 :
case 33 : /* fall-throughs are intentional! */
case 43 : fprintf(out, "<sup>rd</sup>");
break;
default : /* Everything else is XXth */
fprintf(out, "<sup>th</sup>");
break;
}
// Do we have a comment?
if ( strlen(country[which].comment) > 0 )
fprintf(out, " <font size=\"2\">(<i>%s</i>)</font>", country[which].comment);
}
else
{
// we don't got a score!
fprintf(out, "<font color=\"#4F2F2F\">[n/a");
}
fprintf(out, "]</font></big></td>\r\n </tr>\r\n");
fprintf(out, "\
<tr>\r\n\
<td><strong>%s</strong></td>\r\n\
<td><strong>Voted for:</strong></td>\r\n\
<td><strong>Was given points by:</strong></td>\r\n\
</tr>\r\n", country[which].song);
// now we fart around with the sizes to build an appropriate table
// this requires "previous knowledge"...
switch(year)
{
// 360x254 4:3 aspect from Alyson's framegrabber; image source NDR/ARD1
case 2004: tblwid = 360; // 50% of source
trswid = 25;
break;
// 360x214 14:9 aspect from Alyson's framegrabber; image source BBC3/BBC1
case 2005: tblwid = 360; // 50% of source
trswid = 25;
break;
// 360x212 14:9 aspect from Alyson's framegrabber; image source NDR/ARD1
case 2006: tblwid = 360; // 50% of source (note: semi-final was 360x213!)
trswid = 25;
break;
// 384x212 16:9 aspect from RicksPC's capture card; image source BBC3/BBC1
case 2007: tblwid = 384; // 50% of source
trswid = 25;
break;
// 502x282 16:9 aspect from Aiko's capture card; image source BBC3/ BBC1
case 2008: tblwid = 502; // 66% of source
trswid = 15;
break;
// otherwise assume will follow 2008's trend
default: tblwid = 502;
trswid = 15;
break;
}
// sort out the image links
if ( strlen(country[which].image) > 0 )
{
if (country[which].score == -1)
{
if (country[which].semi != 0)
{
// we have a numbered semi-final
// note that it is '1' or '2' (as ASCII number, not decimal value)
if (country[which].semi == '1')
{
// if '1', it is the first semi-final
sprintf(workspace, "%s%d%s%s%s", PATHPREFIX, year,
SEMIPATHONE, country[which].image, SUFFIX);
}
else
{
// otherwise assume the second semi-final; apparently
// the EBU limit is for 45 countries; and god help us
// if we were to require more than two semi=finals!!!
sprintf(workspace, "%s%d%s%s%s", PATHPREFIX, year,
SEMIPATHTWO, country[which].image, SUFFIX);
}
}
else
{
// no numbered semi-final, so we'll do it the old way
sprintf(workspace, "%s%d%s%s%s", PATHPREFIX, year,
SEMIPATHOLD, country[which].image, SUFFIX);
}
}
else
{
// we have a final score, so we must be a finalist country
// hence we take pictures from the grand final content.
sprintf(workspace, "%s%d%s%s%s", PATHPREFIX, year,
FINALPATH, country[which].image, SUFFIX);
}
imgsize = imagesize_getimagesize(workspace);
// we ALWAYS set table width to 360 pixels (should be image width, but...)
fprintf(out, " <tr>\r\n <td width=\"%d\" valign=\"top\"><img src=\"%s\"",
tblwid, workspace);
fprintf(out, " width=\"%d\" height=\"%d\" border=\"0\"></td>\r\n <td valign=\"top\"><pre>\r\n", \
imgsize->width, imgsize->height);
}
else
{
// no image; width is also 360 pixels. This assures consistency.
fprintf(out, " <tr>\r\n <td width=\"%d\" valign=\"middle\" align=\"center\">\r\n",
tblwid);
fprintf(out, " There is no image currently<br>\r\n available for this entry.</td>\r\n");
fprintf(out, " <td valign=\"top\"><pre>\r\n");
}
for (sloop = 0; sloop < 10; sloop++)
{
int val = scorelist[sloop];
char nme[STRLEN] = "";
strcpy(nme, country_matchtoalias(country[which].pointsto[val]));
fprintf(out, "%s%2d - <a href=\"#%s\">%s</a>%s\r\n", \
((val == 12) ? "<b>" : ""), \
val, country[which].pointsto[val], nme, \
((val == 12) ? "</b>" : "") );
}
fprintf(out, " </pre>\r\n </td>\r\n");
if (country[which].score == -1)
{
fprintf(out, " <td width=\"%d%%\" valign=\"top\" align=\"center\">\r\n", trswid);
// fprintf(out, " <i>Nobody, they did not<br> pass the semi-final.</i>\r\n");
fprintf(out, " <i>Nobody, they did not pass the <nobr>semi-final</nobr>.</i>\r\n");
}
else
{
fprintf(out, " <td width=\"%d%%\" valign=\"top\">\r\n", trswid);
fprintf(out, " <font size = \"-1\">\r\n");
country_votedforme(which);
// following on from previous statistics...
// work out what this represents as a %ge of the total vote
pop = ( ((double)country[which].score * (double)100) / (double)(popularity - 58) );
fprintf(out, " <i>The popularity of this song was %.1f%%.</i><br>\r\n", pop);
}
fprintf(out, " </td>\r\n </tr>\r\n</table>\r\n<p> <p>\r\n\r\n\r\n");
return;
}
void country_votedforme(int which)
{
/* Searches for every country that "voted for me", puts them into value order,
then outputs the list as HTML... */
// FOR NOW: Just outputs "voted for" raw and unsorted...
// ASSUMES: country names are cased identically (i.e. won't match "Bosnia" with "bosnia")
int recurse = 0;
int loop = 0;
int accscore = 0;
struct voteddef
{
char name[STRLEN];
char ref[STRLEN];
int points;
};
struct voteddef *voted = NULL;
struct voteddef vtmp[1];
int votedcount = 0;
int sortdone = 0;
int twelves = 0;
voted = memtrace_calloc(countrycount, sizeof(struct voteddef));
if (voted == NULL)
{
fprintf(stderr, "Unable to (re)allocate memory for who-voted-for-me array.\n\n");
exit(EXIT_FAILURE);
}
// Get the 'score' given by each country [is safe, a country cannot vote for itself]
for (recurse = 0; recurse < countrycount; recurse++)
{
// in each score...
for (loop = 1; loop <= 12; loop++)
{
// I think doing a flat 1-12 is less hectic than doing 1...8,10,12
// Entries 9 and 11 will (should!) be blank.
if ( strcmp(country[which].name, country[recurse].pointsto[loop]) == 0 )
{
// We have a match!
strcpy(voted[votedcount].name, country[recurse].name);
strcpy(voted[votedcount].ref , country_matchtoalias(country[recurse].name));
voted[votedcount].points = loop;
accscore += loop;
votedcount++;
}
}
}
// Sort the score table; though bubble sorts are slow and clunky,
// note that we only have a handful of entries to sort through...
// First we sort ALPHABETICALLY by NAME
do
{
sortdone = TRUE;
for (recurse = 0; recurse < (votedcount - 1); recurse++)
{
if ( strcmp(voted[recurse].name, voted[(recurse + 1)].name) > 0 )
{
vtmp[0] = voted[recurse];
voted[recurse] = voted[(recurse + 1)];
voted[(recurse + 1)] = vtmp[0];
sortdone = FALSE;
}
}
} while ( sortdone == FALSE );
// Then we sort NUMERICALLY by SCORE
// This is so we can arrive at lists like:
// 12 Bosnia
// 12 Denmark
// 12 France
// 11 Germany
// [etc]
do
{
sortdone = TRUE;
for (recurse = (votedcount - 1); recurse >= 0; recurse--)
{
if ( voted[recurse].points < voted[(recurse + 1)].points )
{
vtmp[0] = voted[(recurse + 1)];
voted[(recurse + 1)] = voted[recurse];
voted[recurse] = vtmp[0];
sortdone = FALSE;
}
}
} while ( sortdone == FALSE );
// Output the score table, and count number of '12' scores
twelves = 0;
for (recurse = 0; recurse < votedcount; recurse++)
{
// Output given score
//
// Is in "typewriter" style.
// Always two digits wide, if less than ten it is space-padded.
// If 12, it starts a 'bold' section.
fprintf(out, " %s<tt>%s%d</tt>",
((voted[recurse].points == 12) ? "<b>" : ""),
((voted[recurse].points < 10) ? " " : ""),
voted[recurse].points);
// Now linked country hame
fprintf(out, " <a href=\"#%s\">%s</a>", voted[recurse].name, voted[recurse].ref);
// Switch off bold if 12 points, and in any case force a linebreak...
fprintf(out, "%s<br>\r\n", ((voted[recurse].points == 12) ? "</b>" : ""));
if (voted[recurse].points == 12)
twelves++;
}
// Statistics
fprintf(out, " </font>\r\n </td>\r\n </tr>\r\n\r\n");
fprintf(out, " <tr>\r\n <td> </td>\r\n <td colspan = 2>\r\n");
fprintf(out, " <i>%d countr%s voted for this song,</i><br>\r\n",
votedcount, ((votedcount == 1) ? "y" : "ies") );
if (twelves > 0)
fprintf(out, " <i>There %s %d top score%s,</i><br>\r\n",
((twelves == 1) ? "was" : "were"), twelves, ((twelves == 1) ? "" : "s") );
// Popularity follows - but it is worked out in "country_gentable()"
// We'll drop in a verification, useful for checking the data integrity (typos, etc)!
fprintf(out, " <!-- this country scored %d, our survey said... %d -->\r\n",
country[which].score, accscore);
// Release memory claim
memtrace_free(voted);
return;
}
char *country_alias(int which, int title)
{
/* Returns:
If 'title' != 0 and 'titlename' defined : returns 'titlename'
If 'title' != 0 and 'titlename' NOT defined : returns 'country' name
If 'title' == 0 and 'refname' defined : returns 'refname'
If 'title' == 0 and 'refname' NOT defined : returns 'country' name
This is so we can just call Macedonia "Macedonia", and have the 'F.Y.R.' added.
The title name is the name given in the country's title and it must always
be complete, for example:
Bosnia & Herzegovina
The reference name is used elsewhere instead of the given country name,
and can therefore be abbreviated slightly, for example:
Bosnia & H.
Therefore:
Country = "Bosnia"
Title name = "Bosnia & Herzegovina"
Reference name = "Bosnia & H."
IMPORTANT! No HTML munging is applied, so strings must be HTMLised
(in other words, you should write "&" and not just "&").
*/
if (title != 0)
{
if (strlen(country[which].titlename) > 0)
return country[which].titlename;
else
return country[which].name;
}
else
{
if (strlen(country[which].refname) > 0)
return country[which].refname;
else
return country[which].name;
}
}
char *country_matchtoalias(char *ctry)
{
/* Given a name, looks it up, then gets an alias (if any) */
char src[STRLEN] = "";
char dst[STRLEN] = "";
int loop = 0;
int recurse = 0;
for (loop = 0; loop < strlen(ctry); loop++)
src[loop] = tolower(ctry[loop]);
for (recurse = 0; recurse < countrycount; recurse++)
{
for (loop = 0; loop < strlen(ctry); loop++)
dst[loop] = tolower(country[recurse].name[loop]);
if ( strcmp(src, dst) == 0 )
{
// matched!
if (strlen(country[recurse].refname) > 0)
return country[recurse].refname;
else
return country[recurse].name;
}
}
// this happens if we fail to match; is 99.9% the cause
// of a typo like "Cpyrus" or "Azerbajan" or somesuch...
sprintf(dst, "{%s ?}", ctry);
return dst;
}