66 lines
4.3 KiB
Python
66 lines
4.3 KiB
Python
import os
|
|
from pathlib import Path
|
|
import sys,runpy,json,re
|
|
sys.path.insert(0,str(Path(__file__).parent));from framebuffer import Screen,ROOT,shuffle,EVIDENCE
|
|
c=runpy.run_path(str(Path(__file__).with_name('playtest_support.py')));run=json.load(open(os.environ['CAPITAL_TEST_STATE']))['run']['run_id']
|
|
rows=[x.split('\t') for x in (ROOT/'data/states.tsv').read_text().splitlines()]
|
|
order,_=shuffle();evidence=[];score=0;pos=[0]*4
|
|
c['key'](run,'1',8,600)
|
|
for q,state in enumerate(order):
|
|
name=f'mc-q{q+1:02d}-{rows[state][0]}'
|
|
meta=c['capture'](run,name);scr=Screen(str(EVIDENCE)+'/'+name+'.png')
|
|
heading=scr.text(8,19,45);prompt=scr.text(8,145,50)
|
|
assert heading==f'Question {q+1} of 50 Score {score}',(q,heading,score)
|
|
assert prompt==f'{rows[state][1]}: what is its capital?',(q,prompt,rows[state])
|
|
opts=[scr.text(8+(i%2)*156,157+(i//2)*12,25)[2:] for i in range(4)]
|
|
assert len(set(opts))==4 and rows[state][2] in opts,(q,opts)
|
|
correctpos=opts.index(rows[state][2]);pos[correctpos]+=1
|
|
# Confirm selected state's actual framebuffer mask has the expected gold fill.
|
|
src=(ROOT/'src/map_data.c').read_text();d=list(map(int,re.search(r'national_spans\[\] = \{\s*([\d,\s]+)',src)[1].replace('\n','').split(',')));off=list(map(int,re.search(r'national_offsets\[51\] = \{([\d,]+)',src)[1].split(',')))
|
|
points=[(6+x,33+y) for k in range(off[state],off[state+1],3) for y,x0,n in [d[k:k+3]] for x in range(x0,x0+n)]
|
|
gold=sum(scr.guest(x,y)==(255,255,0) for x,y in points)
|
|
assert gold>=len(points)*.8,(q,rows[state][0],gold,len(points))
|
|
right=q%7!=0;selection=correctpos if right else (correctpos+1)%4
|
|
if right:score+=1
|
|
# Read the feedback as soon as it appears, before its automatic timeout.
|
|
held=[str(selection+1)] if q==0 else []
|
|
c['inp'](run,[dict(keys=[str(selection+1)],fields=8),dict(keys=held,fields=80)])
|
|
for attempt in range(35):
|
|
feedback=c['capture'](run,name+'-feedback');fs=Screen(str(EVIDENCE)+'/'+name+'-feedback.png')
|
|
if fs.text(8,158,24) in ('CORRECT!','NOT THIS TIME.'):break
|
|
c['inp'](run,[dict(keys=held,fields=20)])
|
|
assert fs.text(8,158,24)==('CORRECT!' if right else 'NOT THIS TIME.'),(q,fs.text(8,158,24))
|
|
assert fs.text(8,170,50)==f'The capital is {rows[state][2]}.',(q,fs.text(8,170,50))
|
|
assert fs.text(8,183,48)=='Next question follows automatically.'
|
|
evidence.append(dict(question=q+1,code=rows[state][0],state=rows[state][1],options=opts,answer=opts[selection],correct=right,score=score,gold_pixels=gold,expected_state_pixels=len(points),field=meta['field_clock']))
|
|
# No Space/Enter/answer input advances feedback: elapsed guest fields do it.
|
|
c['inp'](run,[dict(keys=held,fields=600)])
|
|
if q==0:
|
|
c['inp'](run,[dict(keys=held,fields=600)])
|
|
c['capture'](run,'held-key-next-question');hs=Screen(str(EVIDENCE)+'/held-key-next-question.png')
|
|
assert hs.text(8,19,45)==f'Question 2 of 50 Score {score}'
|
|
assert hs.text(8,183,48)=='Press 1, 2, 3 or 4'
|
|
c['inp'](run,[dict(keys=[],fields=8)])
|
|
print(q+1,rows[state][0],score,flush=True)
|
|
result=c['capture'](run,'mc-results');s=Screen(str(EVIDENCE)+'/mc-results.png')
|
|
assert s.text(8,48,45)==f'{score} / 50 correct {score*2}%',s.text(8,48,45)
|
|
assert s.text(8,70,45)=='0 correct after spelling correction',s.text(8,70,45)
|
|
assert score==42 and all(pos)
|
|
# Review all eight misses over three pages, preserving answers.
|
|
c['key'](run,'r',8,500)
|
|
misses=[x for x in evidence if not x['correct']]
|
|
for page in range(3):
|
|
c['capture'](run,f'mc-review-{page+1}');s=Screen(f'{EVIDENCE}/mc-review-{page+1}.png')
|
|
assert s.text(8,19,30)==f'Review page {page+1}'
|
|
for i,m in enumerate(misses[page*3:page*3+3]):
|
|
assert s.text(8,39+i*43,45)==m['state']
|
|
assert s.text(8,50+i*43,48)==f"You: {m['answer']}"
|
|
cap=next(x[2] for x in rows if x[0]==m['code'])
|
|
assert s.text(8,61+i*43,48)==f'Capital: {cap}'
|
|
if page<2:c['key'](run,'n',8,500)
|
|
c['key'](run,'b',8,500);c['key'](run,'p',8,500);c['capture'](run,'replay-title');c['key'](run,'q',8,200);final=c['capture'](run,'mc-exit')
|
|
assert 'screen_text' in final or final.get('screen_text_unavailable') is None,final
|
|
json.dump(dict(score=score,positions=pos,questions=evidence,run_id=run,artifact=result['artifact_id'],exit=final),open(str(EVIDENCE)+'/mc-report.json','w'),indent=2)
|
|
print('PASS complete multiple-choice quiz, all 50 masks, automatic feedback advance without Space, held-key protection, 42/50, review, replay, exit.',flush=True)
|
|
c['v'].close()
|