|
| 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 = []) |
0 commit comments