Files
CoCoCapitalQuiz/main.c
T

201 lines
8.0 KiB
C

#include <cmoc.h>
#include <coco.h>
#include <Keyboard.h>
#include <vcc/gime.h>
#include "quiz.h"
#include "ui.h"
static char line[54],answer[ANSWER_SIZE];
static U8 mode,spelling_attempt;
/* Named phases make read-only debugger snapshots useful without altering play. */
U8 phase, current_state;
static void present(void)
{
if(!gime_present()) {gime_close();setHighSpeed(FALSE);printf("Graphics timer stopped.\n");exit(1);}
}
static U8 choose(const char *allowed)
{
U8 c;release_keys();
do {c=next_key();if(c>='a' && c<='z')c-='a'-'A';}while(!strchr(allowed,c));
return c;
}
static void credits(void)
{
phase=5;frame("CREDITS");
text(8,19,"A MAP FOR EVERY STATE",6);
text(8,39,"Map (c) Fla-shop.com",7);
text(8,51,"https://www.fla-shop.com/svg/usa/",2);
text(8,65,"Creative Commons Attribution 4.0",7);
text(8,77,"https://creativecommons.org/licenses/by/4.0/",2);
text(8,94,"Adapted: rasterized, reduced, recolored;",7);
text(8,105,"state masks and enlarged detail views added.",7);
text(8,124,"Font: Pierre Sarrazin, public domain.",2);
text(8,138,"C / CMOC; VCC Plus GIME and keyboard APIs.",2);
text(8,158,"Full sources and notices: assets/CREDITS.md",7);
text(8,180,"SPACE: back",6);present();choose(" ");
}
static U8 title(void)
{
U8 c;
do {
phase=0;frame("50 STATES");
text(8,19,"HOW WELL DO YOU KNOW YOUR CAPITALS?",6);
show_map(49);
/* Right panel is replaced by the mode selector. */
gime_fill_rect(209,32,111,111,0);
text(212,40,"CHOOSE A MODE",6);
text(212,58,"1 MULTIPLE",7);text(224,69,"CHOICE",7);
text(212,89,"2 TYPE THE",7);text(224,100,"CAPITAL",7);
text(212,123,"C CREDITS",2);text(212,136,"Q QUIT",2);
text(8,149,"Choice: 1-4. Type: letters, spaces, Enter.",7);
text(8,160,"Backspace edits. Near spelling: retry/reveal.",2);
text(8,171,"50 questions. Release keys between presses.",2);
text(8,182,"Choice: auto next. Typed: SPACE to continue.",6);
present();c=choose("12CQ");if(c=='C')credits();
}while(c=='C');
return c;
}
static void question_screen(void)
{
phase=1;frame(mode==1?"MULTIPLE CHOICE":"TYPE THE CAPITAL");
sprintf(line,"Question %u of 50 Score %u",(unsigned)question+1,(unsigned)score);
text(8,19,line,7);
show_map(current_state);
gime_fill_rect(0,143,320,49,1);
sprintf(line,"%s: what is its capital?",states[current_state].name);
text(8,145,line,6);
}
static U8 multiple_choice(void)
{
U8 i,c;
quiz_options(current_state);question_screen();
for(i=0;i<4;++i) {
sprintf(line,"%u %s",(unsigned)i+1,states[options[i]].capital);
text(8+(i%2)*156,157+(i/2)*12,line,7);
}
text(8,183,"Press 1, 2, 3 or 4",2);present();c=choose("1234")-'1';
strcpy(answer,states[options[c]].capital);
return options[c]==current_state;
}
static void input_screen(U8 too_long)
{
gime_fill_rect(0,155,320,37,1);text(8,158,answer,7);
text(8+strlen(answer)*6,158,"_",6);
text(8,171,too_long?"39 characters maximum. Backspace to edit.":"Enter submits. Backspace edits. Esc reveals.",2);
text(8,183,"Case and extra spaces do not matter.",2);present();
}
static void input_edit(U8 old_n,U8 new_n)
{
U8 p,first=old_n<new_n?old_n:new_n;
/* Both hidden/displayed pages receive the same tiny edit. Static help and
the map are already drawn, so normal typing never repaints the map. */
for(p=0;p<2;++p) {
gime_fill_rect(8+(unsigned)first*6,158,12,8,1);
text(8+(unsigned)first*6,158,answer+first,7);
text(8+(unsigned)new_n*6,158,"_",6);present();
}
}
static U8 typed_answer(void)
{
U8 c,n=0,result,old_n;
answer[0]=0;spelling_attempt=0;
question_screen();input_screen(0);question_screen();input_screen(0);release_keys();
for(;;) {
c=next_key();release_keys();
if(c==3 || c==27) {strcpy(answer,"[revealed]");return 0;}
if(c==8 || c==127) {old_n=n;if(n)answer[--n]=0;input_edit(old_n,n);}
else if(c==13 || c==10) {
result=classify(current_state,answer);
if(result==2)return 1;
if(result==0)return 0;
question_screen();phase=2;
text(8,158,"That looks close. Check your spelling",6);
text(8,169,"and try again. R: retry V: reveal",7);
text(8,182,"No point awarded yet.",2);present();
c=choose("RV");
if(c=='V')return 0;
spelling_attempt=1;
question_screen();input_screen(0);question_screen();input_screen(0);release_keys();
} else if((c>='a' && c<='z') || (c>='A' && c<='Z') || c==' ' || c=='.' || c=='\t') {
if(n<ANSWER_SIZE-1){old_n=n;answer[n++]=c;answer[n]=0;input_edit(old_n,n);}
else {input_screen(1);input_screen(1);}
}
}
}
static void answer_sound(U8 right)
{
/* Color BASIC SOUND uses built-in audio and normal-speed timing.
Keep the graphics page visible while playing a rising or falling pair. */
setHighSpeed(FALSE);
if(right) {sound(170,2);sound(210,3);}
else {sound(100,3);sound(60,4);}
setHighSpeed(TRUE);
}
static void feedback(U8 right)
{
question_screen();phase=3;
text(8,158,right?"CORRECT!":"NOT THIS TIME.",right?6:5);
sprintf(line,"The capital is %s.",states[current_state].capital);text(8,170,line,7);
text(8,183,mode==1?"Next question follows automatically.":"Release your key, then SPACE to continue.",2);
present();answer_sound(right);
if(mode==1) delay(90); /* 1.5 seconds to read the answer after the tone. */
else choose(" ");
}
static U8 results(void)
{
U8 c,i,page,start,missed=50-score;
for(;;) {
phase=4;frame("YOUR RESULTS");
text(8,19,"JOURNEY COMPLETE",6);
sprintf(line,"%u / 50 correct %u%%",(unsigned)score,(unsigned)score*2);text(8,48,line,7);
sprintf(line,"%u correct after spelling correction",(unsigned)corrections);text(8,70,line,2);
sprintf(line,"%u states to revisit",(unsigned)missed);text(8,92,line,7);
text(8,123,"R REVIEW MISSED STATES",6);
text(8,143,"P PLAY AGAIN",7);text(8,163,"Q QUIT TO BASIC",7);
text(8,183,"Every state asked once. Keep exploring!",2);present();c=choose("RPQ");
if(c!='R')return c;
page=0;start=0;
for(;;) {
frame("MISSED STATES");
sprintf(line,"Review page %u",(unsigned)page+1);text(8,19,line,6);
i=start;c=0;
while(i<50 && c<3) {
if(!correct[order[i]]) {
text(8,39+c*43,states[order[i]].name,6);
sprintf(line,"You: %s",responses[order[i]][0]?(const char *)responses[order[i]]:"[blank]");text(8,50+c*43,line,7);
sprintf(line,"Capital: %s",states[order[i]].capital);text(8,61+c*43,line,2);++c;
}
++i;
}
while(i<50 && correct[order[i]])++i;
if(!missed)text(8,60,"A perfect trip! No missed states.",7);
text(8,180,i<50?"N NEXT PAGE B BACK TO RESULTS":"B BACK TO RESULTS",6);present();
c=choose(i<50?"NB":"B");if(c=='B')break;
start=i;++page;
}
}
}
int main(void)
{
U8 c,right;unsigned seed;
if(!gime_open()){printf("Requires 512 KB CoCo 3 in BASIC.\n");return 1;}
setHighSpeed(TRUE);
gime_set_palette(0,1);gime_set_palette(1,3);gime_set_palette(2,56);
gime_set_palette(3,28);gime_set_palette(4,14);gime_set_palette(5,39);
gime_set_palette(6,54);gime_set_palette(7,63);
for(;;) {
c=title();if(c=='Q')break;mode=c-'0';
seed=entropy ^ getTimer();
#ifdef TEST_SEED
seed=TEST_SEED;
#endif
quiz_start(seed);
for(question=0;question<50;++question) {
current_state=order[question];spelling_attempt=0;
right=mode==1?multiple_choice():typed_answer();
quiz_record(current_state,answer,right,spelling_attempt);feedback(right);
}
if(results()=='Q')break;
}
release_keys();close_input();gime_close();setHighSpeed(FALSE);printf("Thanks for playing Capital Quest!\n");return 0;
}