From d32f7f1b4b36d343d3f1ebe04e790ba7e37e0628 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Fri, 7 Aug 2026 22:03:21 -0400 Subject: [PATCH] Limit triangle generator to right triangles --- README.md | 4 +-- package.json | 2 +- src/c/main.c | 17 +++++---- src/c/triangles.c | 89 ++++++++++++++--------------------------------- src/c/triangles.h | 6 ++-- 5 files changed, 42 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 8111c1a..ae613a9 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Trigalarm -A habit-correcting alarm for heavy sleepers. Cannot be shut off until you describe a triangle. +A habit-correcting alarm for heavy sleepers. Cannot be shut off until you solve for theta in a right triangle. ## The Pitch ### Simple The app only has one page. You can only set one alarm. There is no snooze, there is no weekly schedule, and there is no way to disable the alarm (you'll always have one set - every day - like it or not). ### Persistent -The alarm cannot be stopped until you describe a randomized triangle. Even if you force-close the app, it will re-open itself until you correctly describe the triangle. You must identify the type of triangle and provide the equation to solve for θ. +The alarm cannot be stopped until you solve for theta in a randomized right triangle. Even if you force-close the app, it will re-open itself until you complete the equation. ## Why Trigonometry? Because it requires the perfect amount of thought to activate your brain in the morning. If the app gave you a more typical math problem, you could just mindlessly punch it into a calculator. Trigonometry still requires a base amount of thought ("Do I need sine, cosine, or tangent?") before a calculator is of any use. diff --git a/package.json b/package.json index 8a59a9a..a315d9b 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ ] }, { - "characterRegex": "[?ntype:θ=acuobsrigh¯¹0123456789]", + "characterRegex": "[°ntype:θ=acuobsrigh¯¹0123456789]", "file": "mansalva_regular.ttf", "name": "FONT_MANSALVA_REGULAR_22", "type": "font" diff --git a/src/c/main.c b/src/c/main.c index 0452b14..f607592 100644 --- a/src/c/main.c +++ b/src/c/main.c @@ -36,7 +36,7 @@ static TextLayer *s_drop_txt_under_layer; static Layer *s_division_layer; static Layer *s_alarm_highlight_layer; static MenuLayer *s_drop_menu_layer; -static TriangleLayer *s_triangle_layer; +static RightTriangleLayer *s_triangle_layer; static TextLayer *s_side_a_txt_layer; static TextLayer *s_side_b_txt_layer; static TextLayer *s_side_c_txt_layer; @@ -535,32 +535,33 @@ static void alarm_window_load() { layer_add_child(window_layer, s_alarm_highlight_layer); // triangle - s_triangle_layer = triangle_layer_create(GRect(0, s_top_bar_grect.size.h + 1, PBL_DISPLAY_WIDTH, PBL_DISPLAY_HEIGHT - (s_top_bar_grect.size.h * 2)), GColorBlack); + s_triangle_layer = right_triangle_layer_create(GRect(0, s_top_bar_grect.size.h + 1, PBL_DISPLAY_WIDTH, PBL_DISPLAY_HEIGHT - (s_top_bar_grect.size.h * 2)), GColorBlack); layer_add_child(window_layer, s_triangle_layer->layer); // triangle measurements text layers //// side_a - s_side_a_txt_layer = text_layer_create(GRect(0, s_bottom_bar_grect.origin.y - 27, PBL_DISPLAY_WIDTH, 23)); + s_side_a_txt_layer = text_layer_create(GRect(0, s_bottom_bar_grect.origin.y - 25, PBL_DISPLAY_WIDTH, 23)); text_layer_set_background_color(s_side_a_txt_layer, GColorClear); text_layer_set_font(s_side_a_txt_layer, s_font_mansalva_small); text_layer_set_text_alignment(s_side_a_txt_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(s_side_a_txt_layer)); //// side_b - s_side_b_txt_layer = text_layer_create(GRect(-4, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_WIDTH, 23)); + s_side_b_txt_layer = text_layer_create(GRect(-3, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_WIDTH, 23)); text_layer_set_background_color(s_side_b_txt_layer, GColorClear); text_layer_set_font(s_side_b_txt_layer, s_font_mansalva_small); text_layer_set_text_alignment(s_side_b_txt_layer, GTextAlignmentRight); layer_add_child(window_layer, text_layer_get_layer(s_side_b_txt_layer)); //// side_c - s_side_c_txt_layer = text_layer_create(GRect(4, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_WIDTH, 23)); + s_side_c_txt_layer = text_layer_create(GRect(3, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_WIDTH, 23)); text_layer_set_background_color(s_side_c_txt_layer, GColorClear); text_layer_set_font(s_side_c_txt_layer, s_font_mansalva_small); layer_add_child(window_layer, text_layer_get_layer(s_side_c_txt_layer)); //// display 2 side lengths to the user - static char side_a_buffer[3], side_b_buffer[3], side_c_buffer[3]; + static char side_a_buffer[8], side_b_buffer[8], side_c_buffer[8], angle_A_buffer[8], angle_B_buffer[8], angle_C_buffer[8]; uint8_t ignored_side = rand() % 3; switch (ignored_side) { case 0: + APP_LOG(APP_LOG_LEVEL_DEBUG, "b,c"); snprintf(side_b_buffer, sizeof(side_b_buffer), "%u", s_triangle_layer->side_b); snprintf(side_c_buffer, sizeof(side_c_buffer), "%u", s_triangle_layer->side_c); s_options_over[0] = side_b_buffer; @@ -569,6 +570,7 @@ static void alarm_window_load() { s_options_under[1] = side_c_buffer; break; case 1: + APP_LOG(APP_LOG_LEVEL_DEBUG, "a,c"); snprintf(side_a_buffer, sizeof(side_a_buffer), "%u", s_triangle_layer->side_a); snprintf(side_c_buffer, sizeof(side_c_buffer), "%u", s_triangle_layer->side_c); s_options_over[0] = side_a_buffer; @@ -577,6 +579,7 @@ static void alarm_window_load() { s_options_under[1] = side_c_buffer; break; case 2: + APP_LOG(APP_LOG_LEVEL_DEBUG, "a,b"); snprintf(side_a_buffer, sizeof(side_a_buffer), "%u", s_triangle_layer->side_a); snprintf(side_b_buffer, sizeof(side_b_buffer), "%u", s_triangle_layer->side_b); s_options_over[0] = side_a_buffer; @@ -611,7 +614,7 @@ static void alarm_window_unload() { text_layer_destroy(s_side_a_txt_layer); text_layer_destroy(s_side_b_txt_layer); text_layer_destroy(s_side_c_txt_layer); - triangle_layer_destroy(s_triangle_layer); + right_triangle_layer_destroy(s_triangle_layer); layer_destroy(s_alarm_highlight_layer); layer_destroy(s_division_layer); text_layer_destroy(s_parenthesis_txt_layer); diff --git a/src/c/triangles.c b/src/c/triangles.c index 138d801..1d86f26 100644 --- a/src/c/triangles.c +++ b/src/c/triangles.c @@ -1,27 +1,23 @@ #include "triangles.h" #include -static void triangle_layer_update_proc(Layer *layer, GContext *ctx) { - // retrieve the TriangleLayer pointer stored in the layer's data slot - TriangleLayer *tri = *(TriangleLayer **)layer_get_data(layer); +static void right_triangle_layer_update_proc(Layer *layer, GContext *ctx) { + // retrieve the RightTriangleLayer pointer stored in the layer's data slot + RightTriangleLayer *tri = *(RightTriangleLayer **)layer_get_data(layer); if (!tri) return; GRect bounds = layer_get_bounds(layer); graphics_context_set_stroke_color(ctx, tri->color); - // side_a is always the longest side (horizontal base) + // side a guaranteed longest uint16_t base_side = tri->side_a; - uint16_t left_side = tri->side_c; // C → A (side c connects vertices B and A) - uint8_t left_angle = tri->angle_B; // at vertex B - // calculate height and horizontal apex offset first - int32_t sin_left = sin_lookup(((int32_t)left_angle * TRIG_MAX_ANGLE) / 360); - int32_t cos_left = cos_lookup(((int32_t)left_angle * TRIG_MAX_ANGLE) / 360); - uint16_t height = (uint16_t)(((uint32_t)left_side * (uint32_t)sin_left) / TRIG_MAX_RATIO); - uint16_t apex_offset = (uint16_t)(((uint32_t)left_side * (uint32_t)cos_left) / TRIG_MAX_RATIO); + // compute height and apex offset from side lengths + uint16_t height = (uint16_t)(((uint32_t)tri->side_b * tri->side_c) / tri->side_a); + uint16_t apex_offset = (uint16_t)(((uint32_t)tri->side_c * tri->side_c) / tri->side_a); - // center the triangle both horizontally and vertically + // center both horizontally and vertically uint16_t padded_base = (base_side % 2 == 0) ? base_side : base_side + 1; uint16_t margin = (bounds.size.w / 2) - (padded_base / 2); uint16_t start_x = margin + 1; @@ -30,14 +26,14 @@ static void triangle_layer_update_proc(Layer *layer, GContext *ctx) { uint16_t apex_x = start_x + apex_offset; uint16_t apex_y = base_y - height; - // draw the three sides + // draw sides graphics_draw_line(ctx, GPoint(start_x, base_y), GPoint(end_x, base_y)); graphics_draw_line(ctx, GPoint(start_x, base_y), GPoint(apex_x, apex_y)); graphics_draw_line(ctx, GPoint(end_x, base_y), GPoint(apex_x, apex_y)); } -TriangleLayer *triangle_layer_create(GRect frame, GColor color) { - TriangleLayer *tri = malloc(sizeof(TriangleLayer)); +RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color) { + RightTriangleLayer *tri = malloc(sizeof(RightTriangleLayer)); if (!tri) return NULL; @@ -45,63 +41,30 @@ TriangleLayer *triangle_layer_create(GRect frame, GColor color) { tri->color = color; // determine angles - uint8_t r1, r2; - do { - r1 = 5 + (rand() % 166); // 5–170 (angle_A ≥ 5) - r2 = (r1 + 5) + (rand() % (171 - r1)); // r1+5 … 175 (angles B,C ≥ 5) - } while (r1 == 60 && r2 == 120); // ensure never equilateral + tri->angle_A = 90; + tri->angle_B = rand() % 86; // acute angle at left base vertex + tri->angle_C = 90 - tri->angle_B; // acute angle at right base vertex - tri->angle_A = r1; - tri->angle_B = r2 - r1; - tri->angle_C = 180 - r2; + // choose hypotenuse length randomly + uint16_t min_hyp = (frame.size.w < frame.size.h) ? frame.size.w / 2 : frame.size.h / 2; + uint16_t max_hyp = (frame.size.w < frame.size.h) ? frame.size.w : frame.size.h; + tri->side_a = min_hyp + (rand() % (max_hyp - min_hyp + 1)); - // determine side lengths (law of sines) - int32_t sin_A = sin_lookup(((int32_t)tri->angle_A * TRIG_MAX_ANGLE) / 360); + // legs: side_b = hyp × sin(B), side_c = hyp × sin(C) = hyp × cos(B) int32_t sin_B = sin_lookup(((int32_t)tri->angle_B * TRIG_MAX_ANGLE) / 360); int32_t sin_C = sin_lookup(((int32_t)tri->angle_C * TRIG_MAX_ANGLE) / 360); + tri->side_b = (uint16_t)(((uint32_t)sin_B * tri->side_a) / TRIG_MAX_RATIO); + tri->side_c = (uint16_t)(((uint32_t)sin_C * tri->side_a) / TRIG_MAX_RATIO); - int32_t max_sin = sin_A; - if (sin_B > max_sin) - max_sin = sin_B; - if (sin_C > max_sin) - max_sin = sin_C; - - // longest side randomly chosen - uint16_t min_length_of_longest_side = (frame.size.w < frame.size.h) ? frame.size.w / 2 : frame.size.h / 2; - uint16_t max_length_of_longest_side = (frame.size.w < frame.size.h) ? frame.size.w : frame.size.h; - uint16_t largest_side = min_length_of_longest_side + (rand() % (max_length_of_longest_side - min_length_of_longest_side + 1)); - - tri->side_a = (uint16_t)(((uint32_t)sin_A * largest_side) / (uint32_t)max_sin); - tri->side_b = (uint16_t)(((uint32_t)sin_B * largest_side) / (uint32_t)max_sin); - tri->side_c = (uint16_t)(((uint32_t)sin_C * largest_side) / (uint32_t)max_sin); - - // create layer with room for a pointer-sized data slot & store the TriangleLayer* there - tri->layer = layer_create_with_data(frame, sizeof(TriangleLayer *)); - *(TriangleLayer **)layer_get_data(tri->layer) = tri; - layer_set_update_proc(tri->layer, triangle_layer_update_proc); - - // ensure side_a is always longest - if (tri->side_b > tri->side_a) { - uint16_t tmp_s = tri->side_a; - tri->side_a = tri->side_b; - tri->side_b = tmp_s; - uint8_t tmp_a = tri->angle_A; - tri->angle_A = tri->angle_B; - tri->angle_B = tmp_a; - } - if (tri->side_c > tri->side_a) { - uint16_t tmp_s = tri->side_a; - tri->side_a = tri->side_c; - tri->side_c = tmp_s; - uint8_t tmp_a = tri->angle_A; - tri->angle_A = tri->angle_C; - tri->angle_C = tmp_a; - } + // create layer with room for a pointer-sized data slot & store the RightTriangleLayer* there + tri->layer = layer_create_with_data(frame, sizeof(RightTriangleLayer *)); + *(RightTriangleLayer **)layer_get_data(tri->layer) = tri; + layer_set_update_proc(tri->layer, right_triangle_layer_update_proc); return tri; } -void triangle_layer_destroy(TriangleLayer *tri) { +void right_triangle_layer_destroy(RightTriangleLayer *tri) { if (!tri) return; layer_destroy(tri->layer); diff --git a/src/c/triangles.h b/src/c/triangles.h index 3f1d4c4..4c276be 100644 --- a/src/c/triangles.h +++ b/src/c/triangles.h @@ -14,7 +14,7 @@ typedef struct { uint16_t side_a; uint16_t side_b; uint16_t side_c; -} __attribute__((__packed__)) TriangleLayer; +} __attribute__((__packed__)) RightTriangleLayer; -TriangleLayer *triangle_layer_create(GRect frame, GColor color); -void triangle_layer_destroy(TriangleLayer *tri); +RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color); +void right_triangle_layer_destroy(RightTriangleLayer *tri);