        clues={r.get('word'):r.get('clue','') for r in output.get('clues',[])};words=[r['word'] for r in original['words']];check('六个单词齐全且提示不泄漏英文答案',set(clues)==set(words) and all(w.lower() not in clues.get(w,'').lower() for w in words));grid,placements=_crossword(words)
        reconstructed=[''.join(grid[p['x']+p['dx']*j,p['y']+p['dy']*j] for j in range(len(p['word']))) for p in placements];check('逐位置回读六个单词，交叉字母一致',sorted(reconstructed)==sorted(words));uses=Counter((p['x']+p['dx']*j,p['y']+p['dy']*j) for p in placements for j in range(len(p['word'])));check('六词是相连交叉网格',sum(v>1 for v in uses.values())>=5)
        for answer,name in [(False,'crossword-student.pdf'),(True,'crossword-answer.pdf')]:_crossword_pdf(artifact(name),grid,placements,clues,answer)
        result={'placements':placements,'grid':[{ 'x':x,'y':y,'letter':c} for (x,y),c in sorted(grid.items())],'clues':clues};dump(artifact('crossword-grid.json'),result);check('题目和答案是两份不同原生PDF',sha(folder/'crossword-student.pdf')!=sha(folder/'crossword-answer.pdf') and (folder/'crossword-student.pdf').read_bytes().startswith(b'%PDF'))
