Skip to content

Commit b517550

Browse files
authored
Add Civics Test app (#3224)
1 parent 0d5a43b commit b517550

2 files changed

Lines changed: 394 additions & 0 deletions

File tree

apps/civicstest/civics_test.star

Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
"""
2+
civics_test.star — USCIS Civics Test for Tidbyt (Pixlet / Starlark)
3+
4+
Displays one USCIS civics question per day (changes daily):
5+
1) Waving American flag (sine-wave pixel animation)
6+
2) "QUESTION" title card
7+
3) The question text (scrolls vertically if long)
8+
4) "ANSWER" title card
9+
5) The answer text (scrolls vertically if long)
10+
11+
Run locally: pixlet serve civics_test.star
12+
Render to web: pixlet render civics_test.star
13+
Push to device:
14+
pixlet render civics_test.star
15+
pixlet push $TIDBYT_DEVICE_ID civics_test.webp --api-token $TIDBYT_API_KEY --installation-id civicstest
16+
"""
17+
18+
load("render.star", "render")
19+
load("schema.star", "schema")
20+
load("time.star", "time")
21+
22+
# ---------------------------------------------------------------------------
23+
# Data: 128 USCIS civics questions with one concise, screen-friendly answer.
24+
# (Questions with "answers will vary" / "see uscis.gov" use a short stand-in.)
25+
# ---------------------------------------------------------------------------
26+
QUESTIONS = [
27+
("What is the form of government of the United States?", "Republic"),
28+
("What is the supreme law of the land?", "The U.S. Constitution"),
29+
("Name one thing the U.S. Constitution does.", "Forms the government"),
30+
("What does \"We the People\" mean?", "People govern themselves"),
31+
("How are changes made to the U.S. Constitution?", "Amendments"),
32+
("What does the Bill of Rights protect?", "Basic rights of Americans"),
33+
("How many amendments does the U.S. Constitution have?", "Twenty-seven (27)"),
34+
("Why is the Declaration of Independence important?", "It says all people are created equal"),
35+
("What founding document said the colonies were free from Britain?", "Declaration of Independence"),
36+
("Name two important ideas from the Declaration and Constitution.", "Liberty and equality"),
37+
("\"Life, Liberty, and the pursuit of Happiness\" are in what document?", "Declaration of Independence"),
38+
("What is the economic system of the United States?", "Capitalism / free market"),
39+
("What is the rule of law?", "No one is above the law"),
40+
("Many documents influenced the Constitution. Name one.", "Declaration of Independence"),
41+
("There are three branches of government. Why?", "So one part doesn't get too powerful"),
42+
("Name the three branches of government.", "Legislative, executive, judicial"),
43+
("The President is in charge of which branch?", "Executive branch"),
44+
("What part of the federal government writes laws?", "Congress"),
45+
("What are the two parts of the U.S. Congress?", "Senate and House"),
46+
("Name one power of the U.S. Congress.", "Writes laws"),
47+
("How many U.S. senators are there?", "One hundred (100)"),
48+
("How long is a term for a U.S. senator?", "Six (6) years"),
49+
("Who is one of your state's U.S. senators now?", "(Varies) your state's senator"),
50+
("How many voting members are in the House of Representatives?", "Four hundred thirty-five (435)"),
51+
("How long is a term for a member of the House?", "Two (2) years"),
52+
("Why do representatives serve shorter terms than senators?", "To follow public opinion more closely"),
53+
("How many senators does each state have?", "Two (2)"),
54+
("Why does each state have two senators?", "Equal representation"),
55+
("Name your U.S. representative.", "(Varies) your representative"),
56+
("Who is the Speaker of the House now?", "See uscis.gov/citizenship/testupdates"),
57+
("Who does a U.S. senator represent?", "People of their state"),
58+
("Who elects U.S. senators?", "Citizens of their state"),
59+
("Who does a member of the House represent?", "People of their district"),
60+
("Who elects members of the House?", "Citizens of their district"),
61+
("Some states have more representatives than others. Why?", "Because of the state's population"),
62+
("The President is elected for how many years?", "Four (4) years"),
63+
("The President can serve only two terms. Why?", "Because of the 22nd Amendment"),
64+
("What is the name of the President now?", "See uscis.gov/citizenship/testupdates"),
65+
("What is the name of the Vice President now?", "See uscis.gov/citizenship/testupdates"),
66+
("If the president can no longer serve, who becomes president?", "The Vice President"),
67+
("Name one power of the president.", "Signs bills into law"),
68+
("Who is Commander in Chief of the U.S. military?", "The President"),
69+
("Who signs bills to become laws?", "The President"),
70+
("Who vetoes bills?", "The President"),
71+
("Who appoints federal judges?", "The President"),
72+
("The executive branch has many parts. Name one.", "The Cabinet"),
73+
("What does the President's Cabinet do?", "Advises the President"),
74+
("What are two Cabinet-level positions?", "Secretary of State; Attorney General"),
75+
("Why is the Electoral College important?", "It decides who is elected president"),
76+
("What is one part of the judicial branch?", "Supreme Court"),
77+
("What does the judicial branch do?", "Reviews laws"),
78+
("What is the highest court in the United States?", "Supreme Court"),
79+
("How many seats are on the Supreme Court?", "Nine (9)"),
80+
("How many Supreme Court justices are usually needed to decide a case?", "Five (5)"),
81+
("How long do Supreme Court justices serve?", "For life"),
82+
("Supreme Court justices serve for life. Why?", "To be independent of politics"),
83+
("Who is the Chief Justice of the United States now?", "See uscis.gov/citizenship/testupdates"),
84+
("Name one power only for the federal government.", "Print money / declare war"),
85+
("Name one power only for the states.", "Provide schooling / police"),
86+
("What is the purpose of the 10th Amendment?", "Powers not given to the U.S. belong to the states/people"),
87+
("Who is the governor of your state now?", "(Varies) your governor"),
88+
("What is the capital of your state?", "(Varies) your state capital"),
89+
("Describe one amendment about who can vote.", "Citizens 18 and older can vote"),
90+
("Who can vote, run for federal office, and serve on a jury?", "U.S. citizens"),
91+
("What are three rights of everyone living in the U.S.?", "Speech, religion, assembly"),
92+
("What do we show loyalty to in the Pledge of Allegiance?", "The United States / the flag"),
93+
("Name two promises new citizens make in the Oath of Allegiance.", "Defend the Constitution; obey U.S. laws"),
94+
("How can people become United States citizens?", "Be born in the U.S. or naturalize"),
95+
("What are two examples of civic participation?", "Vote; join a political party"),
96+
("What is one way Americans can serve their country?", "Vote; pay taxes; obey the law"),
97+
("Why is it important to pay federal taxes?", "Required by law; civic duty"),
98+
("Why should men 18-25 register for Selective Service?", "Required by law"),
99+
("The colonists came to America for many reasons. Name one.", "Freedom; religious freedom"),
100+
("Who lived in America before the Europeans arrived?", "American Indians / Native Americans"),
101+
("What group of people was taken and sold as slaves?", "Africans"),
102+
("What war did Americans fight to win independence from Britain?", "American Revolution"),
103+
("Name one reason Americans declared independence from Britain.", "Taxation without representation"),
104+
("Who wrote the Declaration of Independence?", "(Thomas) Jefferson"),
105+
("When was the Declaration of Independence adopted?", "July 4, 1776"),
106+
("Name one important event of the American Revolution.", "Declaration of Independence"),
107+
("There were 13 original states. Name five.", "Virginia, New York, Georgia, Delaware, Massachusetts"),
108+
("What founding document was written in 1787?", "The U.S. Constitution"),
109+
("Name one writer of the Federalist Papers.", "(James) Madison / (Alexander) Hamilton / (John) Jay"),
110+
("Why were the Federalist Papers important?", "They helped people support the Constitution"),
111+
("Benjamin Franklin is famous for many things. Name one.", "U.S. diplomat; first Postmaster General"),
112+
("George Washington is famous for many things. Name one.", "First president of the United States"),
113+
("Thomas Jefferson is famous for many things. Name one.", "Wrote the Declaration of Independence"),
114+
("James Madison is famous for many things. Name one.", "\"Father of the Constitution\""),
115+
("Alexander Hamilton is famous for many things. Name one.", "First Secretary of the Treasury"),
116+
("What territory did the U.S. buy from France in 1803?", "Louisiana Territory"),
117+
("Name one war fought by the U.S. in the 1800s.", "Civil War"),
118+
("Name the U.S. war between the North and the South.", "The Civil War"),
119+
("The Civil War had many important events. Name one.", "Emancipation Proclamation"),
120+
("Abraham Lincoln is famous for many things. Name one.", "Freed the slaves; 16th president"),
121+
("What did the Emancipation Proclamation do?", "Freed the slaves in the Confederacy"),
122+
("What U.S. war ended slavery?", "The Civil War"),
123+
("What amendment made people born in the U.S. citizens?", "14th Amendment"),
124+
("When did all men get the right to vote?", "With the 15th Amendment (1870)"),
125+
("Name one leader of the women's rights movement in the 1800s.", "Susan B. Anthony"),
126+
("Name one war fought by the U.S. in the 1900s.", "World War II"),
127+
("Why did the U.S. enter World War I?", "Germany attacked U.S. ships"),
128+
("When did all women get the right to vote?", "1920 (19th Amendment)"),
129+
("What was the Great Depression?", "Longest economic recession in modern history"),
130+
("When did the Great Depression start?", "The stock market crash of 1929"),
131+
("Who was president during the Depression and WWII?", "(Franklin) Roosevelt"),
132+
("Why did the U.S. enter World War II?", "Japan attacked Pearl Harbor"),
133+
("Dwight Eisenhower is famous for many things. Name one.", "General in WWII; 34th president"),
134+
("Who was the U.S. main rival during the Cold War?", "Soviet Union / USSR"),
135+
("During the Cold War, what was one main U.S. concern?", "Communism"),
136+
("Why did the U.S. enter the Korean War?", "To stop the spread of communism"),
137+
("Why did the U.S. enter the Vietnam War?", "To stop the spread of communism"),
138+
("What did the civil rights movement do?", "Fought to end racial discrimination"),
139+
("Martin Luther King, Jr. is famous for many things. Name one.", "Fought for civil rights"),
140+
("Why did the U.S. enter the Persian Gulf War?", "To force Iraq's military out of Kuwait"),
141+
("What major event happened on September 11, 2001?", "Terrorists attacked the United States"),
142+
("Name one U.S. military conflict after 9/11.", "War in Afghanistan (War on Terror)"),
143+
("Name one American Indian tribe in the United States.", "Cherokee, Navajo, Sioux, Apache, Hopi"),
144+
("Name one example of an American innovation.", "The light bulb"),
145+
("What is the capital of the United States?", "Washington, D.C."),
146+
("Where is the Statue of Liberty?", "New York Harbor (Liberty Island)"),
147+
("Why does the flag have 13 stripes?", "There were 13 original colonies"),
148+
("Why does the flag have 50 stars?", "One star for each of the 50 states"),
149+
("What is the name of the national anthem?", "The Star-Spangled Banner"),
150+
("\"E Pluribus Unum\" means what?", "Out of many, one"),
151+
("What is Independence Day?", "A holiday celebrating U.S. independence"),
152+
("Name three national U.S. holidays.", "Independence Day, Thanksgiving, Memorial Day"),
153+
("What is Memorial Day?", "Honors soldiers who died in service"),
154+
("What is Veterans Day?", "Honors people who served in the military"),
155+
]
156+
157+
# ---------------------------------------------------------------------------
158+
# Colors
159+
# ---------------------------------------------------------------------------
160+
RED = "#b22234"
161+
WHITE = "#ffffff"
162+
NAVY = "#3c3b6e"
163+
BLACK = "#000000"
164+
GOLD = "#ffd24a"
165+
166+
FRAME_MS = 100
167+
FPS = 1000 // FRAME_MS
168+
169+
DISPLAY_W = 64
170+
DISPLAY_H = 32
171+
CONTENT_W = DISPLAY_W - 2 # 1px padding each side
172+
CONTENT_H = DISPLAY_H - 2
173+
174+
CANTON_W = 26
175+
CANTON_H = 17
176+
177+
SCROLL_THRESHOLD = 60 # chars beyond which text scrolls instead of centers
178+
179+
TOTAL_S = 15 # total animation duration in seconds
180+
181+
# Percentage of TOTAL_S per section — must sum to 100
182+
FLAG_PCT = 10 # ~1.5s
183+
LABEL_PCT = 5 # ~0.75s each (applied twice)
184+
Q_PCT = 40 # ~6s
185+
A_PCT = 40 # ~6s
186+
187+
# ---------------------------------------------------------------------------
188+
# Question pick — stable for the day, changes daily
189+
# ---------------------------------------------------------------------------
190+
def pick_question():
191+
day = int(time.now().format("20060102"))
192+
return QUESTIONS[day % len(QUESTIONS)]
193+
194+
# ---------------------------------------------------------------------------
195+
# Flag rendering — pixel-column sine wave animation
196+
# ---------------------------------------------------------------------------
197+
198+
# sin(2*pi*i/64) * 256 for i in 0..63 (integer lookup, no math lib needed)
199+
SIN64 = [
200+
0,
201+
25,
202+
50,
203+
74,
204+
98,
205+
121,
206+
142,
207+
162,
208+
181,
209+
198,
210+
213,
211+
226,
212+
237,
213+
245,
214+
251,
215+
255,
216+
256,
217+
255,
218+
251,
219+
245,
220+
237,
221+
226,
222+
213,
223+
198,
224+
181,
225+
162,
226+
142,
227+
121,
228+
98,
229+
74,
230+
50,
231+
25,
232+
0,
233+
-25,
234+
-50,
235+
-74,
236+
-98,
237+
-121,
238+
-142,
239+
-162,
240+
-181,
241+
-198,
242+
-213,
243+
-226,
244+
-237,
245+
-245,
246+
-251,
247+
-255,
248+
-256,
249+
-255,
250+
-251,
251+
-245,
252+
-237,
253+
-226,
254+
-213,
255+
-198,
256+
-181,
257+
-162,
258+
-142,
259+
-121,
260+
-98,
261+
-74,
262+
-50,
263+
-25,
264+
]
265+
266+
# Cumulative y-bounds for the 13 flag stripes (heights sum to 32px)
267+
STRIPE_BOUNDS = [0, 3, 5, 8, 10, 13, 15, 18, 20, 23, 25, 28, 30, 32]
268+
STRIPE_COLORS = [RED, WHITE, RED, WHITE, RED, WHITE, RED, WHITE, RED, WHITE, RED, WHITE, RED]
269+
270+
def stripe_color(y):
271+
for i in range(len(STRIPE_COLORS)):
272+
if y < STRIPE_BOUNDS[i + 1]:
273+
return STRIPE_COLORS[i]
274+
return RED
275+
276+
def flag_pixel(x, y):
277+
if y < 0 or y >= DISPLAY_H:
278+
return BLACK
279+
if x < CANTON_W and y < CANTON_H:
280+
# Canton: NAVY with a 4px-grid of white star dots (2px inset from top-left)
281+
cx = x - 2
282+
cy = y - 2
283+
if cx >= 0 and cy >= 0 and cx % 4 == 0 and cy % 4 == 0:
284+
return WHITE
285+
return NAVY
286+
return stripe_color(y)
287+
288+
def flag_column(x, frame):
289+
# 2 sine wavelengths across the 64px width; wave travels right each frame
290+
phase = (x * 2 - frame * 4) % 64
291+
d = SIN64[phase] * 3 // 256 # ±3px vertical offset
292+
boxes = []
293+
run_color = ""
294+
run_len = 0
295+
for r in range(DISPLAY_H):
296+
c = flag_pixel(x, r - d)
297+
if c == run_color:
298+
run_len += 1
299+
else:
300+
if run_len > 0:
301+
boxes.append(render.Box(width = 1, height = run_len, color = run_color))
302+
run_color = c
303+
run_len = 1
304+
if run_len > 0:
305+
boxes.append(render.Box(width = 1, height = run_len, color = run_color))
306+
return render.Column(children = boxes)
307+
308+
def flag_wave_frame(frame):
309+
columns = []
310+
for x in range(DISPLAY_W):
311+
columns.append(flag_column(x, frame))
312+
return render.Row(children = columns)
313+
314+
# ---------------------------------------------------------------------------
315+
# Text screens
316+
# ---------------------------------------------------------------------------
317+
def label_screen(label, color):
318+
return render.Box(
319+
width = DISPLAY_W,
320+
height = DISPLAY_H,
321+
color = BLACK,
322+
child = render.Text(label, font = "tb-8", color = color),
323+
)
324+
325+
def content_screen(text, color):
326+
wrapped = render.WrappedText(content = text, width = CONTENT_W, font = "tom-thumb", color = color, align = "center")
327+
if len(text) <= SCROLL_THRESHOLD:
328+
# Short enough to fit: center vertically within the content area
329+
inner = render.Box(
330+
width = CONTENT_W,
331+
height = CONTENT_H,
332+
child = render.Column(
333+
main_align = "center",
334+
cross_align = "center",
335+
children = [wrapped],
336+
),
337+
)
338+
else:
339+
# Too long to guarantee fit: scroll vertically
340+
inner = render.Marquee(height = CONTENT_H, scroll_direction = "vertical", child = wrapped)
341+
return render.Box(
342+
width = DISPLAY_W,
343+
height = DISPLAY_H,
344+
color = BLACK,
345+
child = render.Padding(pad = 1, child = inner),
346+
)
347+
348+
# ---------------------------------------------------------------------------
349+
# Main
350+
# ---------------------------------------------------------------------------
351+
def add_held_frames(frames, screen, count):
352+
for _ in range(count):
353+
frames.append(screen)
354+
355+
def main(_):
356+
if FLAG_PCT + LABEL_PCT * 2 + Q_PCT + A_PCT != 100:
357+
fail("Time percentages must sum to 100")
358+
359+
q, a = pick_question()
360+
361+
total_frames = TOTAL_S * FPS
362+
flag_frames = total_frames * FLAG_PCT // 100
363+
label_hold = total_frames * LABEL_PCT // 100
364+
q_hold = total_frames * Q_PCT // 100
365+
a_hold = total_frames * A_PCT // 100
366+
367+
frames = []
368+
for f in range(flag_frames):
369+
frames.append(flag_wave_frame(f))
370+
371+
add_held_frames(frames, label_screen("QUESTION", GOLD), label_hold)
372+
add_held_frames(frames, content_screen(q, WHITE), q_hold)
373+
add_held_frames(frames, label_screen("ANSWER", GOLD), label_hold)
374+
add_held_frames(frames, content_screen(a, WHITE), a_hold)
375+
376+
return render.Root(
377+
delay = FRAME_MS,
378+
show_full_animation = True,
379+
child = render.Animation(children = frames),
380+
)
381+
382+
# ---------------------------------------------------------------------------
383+
# Schema (configuration in the Tidbyt app)
384+
# ---------------------------------------------------------------------------
385+
def get_schema():
386+
return schema.Schema(version = "1", fields = [])

apps/civicstest/manifest.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
id: civics-test
3+
name: Civics Test
4+
summary: Daily civics test question
5+
desc: Displays a random question and answer from the 128 USCIS civics test questions. Great for studying for the U.S. citizenship test.
6+
author: hachibu
7+
fileName: civics_test.star
8+
packageName: civicstest

0 commit comments

Comments
 (0)