From 38c882decf01a18a153ff590f166821dae69de31 Mon Sep 17 00:00:00 2001 From: San Jacobs Date: Wed, 17 May 2023 02:45:53 +0200 Subject: Still making epic strides --- src/main.odin | 17 +++- src/time.odin | 266 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 237 insertions(+), 46 deletions(-) diff --git a/src/main.odin b/src/main.odin index d4240ce..2030c2f 100644 --- a/src/main.odin +++ b/src/main.odin @@ -4,6 +4,10 @@ import "core:fmt" main :: proc() { test() + new_workday({0, 0, 1, 1, 1}, + {00, 08, 5, 5, 2023}, + {00, 23, 5, 5, 2023}, + {30, 21, 5, 5, 2023}) } test :: proc() { @@ -14,11 +18,11 @@ test :: proc() { fmt.println(toString(test_delta)) test_delta = {0, 0, 0} fmt.println(toString(test_delta)) - test_delta = {45, 12, 1} + test_delta = {0, 12, 2} fmt.printf("I've been waiting for %s! That's a long time!\n", toString(test_delta)) start: Moment = {45, 8, 20, 4, 2023} - end: Moment = {45, 8, 20, 4, 2023} + end: Moment = add(start, test_delta) block: Timeblock = {start, end, 0, ""} fmt.println(toString(block)) @@ -36,6 +40,7 @@ test :: proc() { fmt.println(toString(test_moment)) + fmt.println("\n--- TESTING OPERATIONS ---") fmt.printf("So far test_moment, holds: %s.\n", toString(test_moment)) @@ -46,13 +51,19 @@ test :: proc() { - fmt.println("\n--- TESTING DELTA ---") + fmt.println("\n--- TESTING DELTA & SORTABLE ---") + + fmt.printf("The sortable() version of %s is %i.\n", toString(test_moment), sortable(test_moment)) + fmt.printf("The sortable() version of %s is %i.\n", toString(test_delta), sortable(test_delta)) + fmt.printf("The diff() between %s and %s is %s.\n", toString(test_moment), toString(add(test_moment, test_delta)), toString(diff(test_moment, add(test_moment, test_delta)))) + //for i: int=1; i < 14; i += 1 { // fmt.println(days_in(i, 2023)) //} + fmt.println("") } \ No newline at end of file diff --git a/src/time.odin b/src/time.odin index d6f6046..6acfc20 100644 --- a/src/time.odin +++ b/src/time.odin @@ -5,6 +5,7 @@ import "core:fmt" import "core:strings" import "core:strconv" import "core:slice" +import "core:sort" // // --- STRUCTURES --- @@ -50,10 +51,108 @@ Workday :: struct { total_timeblocks : int, } + // -// --- BASIC OPERATIONS --- +// --- MAJOR PROCEDURES --- // +new_workday :: proc(previous_wrap : Moment, + calltime : Moment, + wraptime : Moment, + planned_wraptime : Moment) -> (workday: Workday) { + + workday.call = calltime + workday.wrap = wraptime + workday.planned_wrap = planned_wraptime + + using workday + + initial_block: Timeblock = {call, + // Paragraph 6.7 says that up to 2 hours of unused warned overtime counts as worktime, + // though so that at least one hour of the unused overtime is not counted. + // (It's unclear if an 8-hour day that ends 3 hours in counts as having 5 hours of unused overtime) + max(clamp(sub(planned_wrap, {0, 1, 0}), wrap, add(planned_wrap, {0, 2, 0})), + add(call, {0, 4, 0})), 0, ""} + // ^ Minimum 4 hour day ^ + + sp_length :: 11 + splitpoints:= [sp_length]Moment{ // --$-- Points where the price may change --$-- // + + // TODO: Replace this terribleness with a system for parsing simple, user-editable rulseset-files + + add(previous_wrap, {0, 10, 0}), // Sleepbreach, 10 hours after previous wrap, aka. turnover + {0, 5, call.day, call.month, call.year}, // 2 hours before 7, aka 5 + {0, 6, call.day, call.month, call.year}, // 6 in the morning + add(call, {0, 8, 0}), // Normal 8 hours of work + add(call, {0, 9, 0}), // 1st hour of overtime is over + add(call, {0, 11, 0}), // 3rd hour of overtime is over + planned_wraptime, // End of warned overtime + add(call, {0, 14, 0}), // The 14-hour mark + {0, 22, call.day, call.month, call.year}, // 22:00 in the evening + add({0, 23, call.day, call.month, call.year}, {0, 1, 0}), // Midnight + add({0, 22, call.day, call.month, call.year}, {0, 7, 0}), // 06:00 the next morning + } + + // Eliminate planned wrap, if it occurs within normal 8-hour period. + // This is to make sure the first period of time becomes a pure 8 hours, + // which makes detecting the main section of the workday easier. + if sortable(splitpoints[6]) < sortable(splitpoints[3]) { + splitpoints[6] = splitpoints[3]; + } + + splitpoints_sorted: [sp_length]Moment = splitpoints + slice.sort_by(splitpoints_sorted[:], lessMoment) + + working_block: Timeblock = initial_block + + j: int = 0 + for each_moment in splitpoints_sorted { + // If each splitpoint moment is within the workday, and is not equal to the start of the current block + if sortable(each_moment) > sortable(call) && sortable(each_moment) < sortable(wrap) && each_moment != initial_block.start { + blocks[j], working_block = timesplit(working_block, each_moment) + j += 1 + fmt.println("Split and wrote:", j) + } + } + blocks[j] = working_block + j += 1 + total_timeblocks = j + + slice.sort_by(blocks[:], lessTimeblock) + + for each_block, i in blocks { + fmt.printf("Block %2i: %s\n", i+1, toString(each_block)) + } + + // TODO: + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + // BIG BLOCK OF RULES N SHIT GOES HERE + + return +} + windIndividual :: proc(input_moment: ^Moment, minutes: int, hours: int, @@ -114,6 +213,29 @@ windDelta :: proc(moment: ^Moment, delta: Delta) { } wind :: proc{windIndividual, windDelta} +timesplit :: proc(block: Timeblock, splitpoint: Moment) -> (first_half: Timeblock, second_half: Timeblock) { + // Splits a timeblock at splitpoint. + + if sortable(splitpoint) < sortable(block.start) || + sortable(splitpoint) > sortable(block.end) || + splitpoint == block.start || splitpoint == block.end { + fmt.println("WHOOPS: Splitpoint is outside timeblock range!") + fmt.println("Timeblock:", toString(block)) + fmt.println("Splitpoint:", toString(splitpoint)) + second_half = block + return + } + + first_half = {block.start, splitpoint, block.valuefactor, block.price_reason} + second_half = {splitpoint, block.end, block.valuefactor, block.price_reason} + + return +} + +// +// --- BASIC OPERATIONS --- +// + add :: proc(moment: Moment, delta: Delta) -> (output: Moment) { output = moment @@ -126,43 +248,126 @@ sub :: proc(moment: Moment, delta: Delta) -> (output: Moment) { wind(&output, minutes*-1, hours*-1, days*-1) return } +maxMoment :: proc(moment_a: Moment, moment_b: Moment) -> Moment { + if sortable(moment_a) > sortable(moment_b) do return moment_a + return moment_b +} +maxDelta :: proc(delta_a: Delta, delta_b: Delta) -> Delta { + if sortable(delta_a) > sortable(delta_b) do return delta_a + return delta_b +} +max :: proc{maxDelta, maxMoment} -gtMoment :: proc(moment_a: Moment, moment_b: Moment) -> bool { - // TODO: This is wrong - return true +minMoment :: proc(moment_a: Moment, moment_b: Moment) -> Moment { + if sortable(moment_a) < sortable(moment_b) do return moment_a + return moment_b } -gtDelta :: proc(delta_a: Delta, delta_b: Delta) -> bool { - // TODO: This is wrong - return true +minDelta :: proc(delta_a: Delta, delta_b: Delta) -> Delta { + if sortable(delta_a) < sortable(delta_b) do return delta_a + return delta_b } -gt :: proc{gtMoment, gtDelta} +min :: proc{minDelta, minMoment} + +clampMoment :: proc(moment: Moment, moment_min: Moment, moment_max: Moment) -> Moment { + return min(max(moment, moment_min), moment_max) +} +clampDelta :: proc(delta: Delta, delta_min: Delta, delta_max: Delta) -> Delta { + return min(max(delta, delta_min), delta_max) +} +clamp :: proc{clampMoment, clampDelta} + +greatMoment :: proc(moment_a: Moment, moment_b: Moment) -> bool { + return bool(sortable(moment_a) > sortable(moment_b)) +} +greatDelta :: proc(delta_a: Delta, delta_b: Delta) -> bool { + return bool(sortable(delta_a) > sortable(delta_b)) +} +great :: proc{greatMoment, greatDelta} + +lessMoment :: proc(moment_a: Moment, moment_b: Moment) -> bool { + return bool(sortable(moment_a) < sortable(moment_b)) +} +lessDelta :: proc(delta_a: Delta, delta_b: Delta) -> bool { + return bool(sortable(delta_a) < sortable(delta_b)) +} +lessTimeblock :: proc(block_a: Timeblock, block_b: Timeblock) -> bool { + if block_b.start == {0, 0, 0, 0, 0} do return true + if block_a.start == {0, 0, 0, 0, 0} do return false + return bool(sortable(block_a.start) < sortable(block_b.start)) +} +less :: proc{lessMoment, lessDelta, lessTimeblock} + diff :: proc(moment_a: Moment, moment_b: Moment) -> (acc: Delta) { - - acc = {0, 0, 0} - if moment_a == moment_b do return + // TODO: Finish writing this // Uses what I call an accumulator-decumulator design // Count how long it takes to approach a benchmark, // and that count is the difference - reverse: bool = gt(moment_b, moment_a) + acc = {0, 0, 0} + if moment_a == moment_b do return + // smallest operand becomes benchmark to approach + reverse: bool = sortable(moment_a) < sortable(moment_b) + bench : Moment + dec : Moment + if reverse { + bench = moment_a + dec = moment_b + } else { + bench = moment_b + dec = moment_a + } - // TODO: Finish writing this + // It is possible to write something that does this in months at a time, instead of days, + // which would be faster, but I am not expecting to have to do this with such + // long periods of time, so screw that. + for ((dec.year - bench.year) > 1 || + (dec.month - bench.month) > 1 || + (dec.day - bench.day) > 1) { + wind(&dec, 0, 0, -1) + acc.days += 1 + } + for (dec.hours - bench.hours > 1) { + wind(&dec, 0, -1, 0) + acc.hours += 1 + } + for acc.hours > 23 { + acc.hours -= 24 + acc.days += 1 + } + + for dec != bench { + wind(&dec, -1, 0, 0) + acc.minutes += 1 + } + for acc.minutes > 59 { + acc.minutes -= 60 + acc.hours += 1 + } + + // Repeating this is a little bit ugly, but it works + for acc.hours > 23 { + acc.hours -= 24 + acc.days += 1 + } return } -sortableTimeDelta :: proc(delta: Delta) -> int { +sortableTimeDelta :: proc(delta: Delta) -> (output: u64) { using delta - return strconv.atoi(fmt.tprintf("%2i%2i%2i", days, hours, minutes)) + output, _ = strconv.parse_u64(fmt.tprintf("1%3i%2i%2i", days, hours, minutes)) + return } -sortableTimeMoment :: proc(moment: Moment) -> int { +sortableTimeMoment :: proc(moment: Moment) -> (output: u64) { using moment - return strconv.atoi(fmt.tprintf("%4i%2i%2i%2i%2i", year, month, day, hours, minutes)) + output, _ = strconv.parse_u64(fmt.tprintf("%4i%2i%2i%2i%2i", year, month, day, hours, minutes)) + return } +sortable :: proc{sortableTimeMoment, sortableTimeDelta} deltaToString :: proc(delta: Delta) -> (output: string) { using delta @@ -225,7 +430,7 @@ momentToString :: proc(moment: Moment) -> (output: string) { cat_array: [dynamic]string - output = fmt.tprintf("%i-%2i-%2i %2i:%2i", year, month, day, hours, minutes) + output = fmt.tprintf("%4i-%2i-%2i %2i:%2i", year, month, day, hours, minutes) return } @@ -249,11 +454,6 @@ clockprintTimeblock :: proc(block: Timeblock) -> string { } timeprint :: proc{clockprintTimeblock, clockprintMoment} - -// -// --- PROCEDURES --- -// - days_in :: proc(month: int, year: int) -> int { switch month { case 1: @@ -288,23 +488,3 @@ days_in :: proc(month: int, year: int) -> int { assert(month < 13 && month > 0) return 30 } - -new_workday :: proc(previous_wrap : Moment, - calltime : Moment, - wraptime : Moment, - planned_wraptime : Moment) -> (workday: Workday) { - - workday.call = calltime - workday.wrap = wraptime - workday.planned_wrap = planned_wraptime - - /* - initial_block: Timeblock = { - math.max( - math.clamp(), - ) - } - */ - - return -} -- cgit v1.2.1