From 46fdf2827e80e1dab0efd7173c9f518af8e38831 Mon Sep 17 00:00:00 2001 From: San Jacobs Date: Fri, 19 May 2023 04:04:33 +0200 Subject: Productivity is my middle name --- ruleset_test.ruleset | 4 ++ src/time.odin | 176 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 142 insertions(+), 38 deletions(-) diff --git a/ruleset_test.ruleset b/ruleset_test.ruleset index b7f33ee..9aa7e54 100644 --- a/ruleset_test.ruleset +++ b/ruleset_test.ruleset @@ -1,2 +1,6 @@ calltime + 8 < plannedwrap = 150% +That evaluation-procedure IS the rulseset. +A simple switch-case statement will let us pick between which ruleset to use. +It will take basic info as arguments (previous_wrap, calltime, wraptime, planned_wrap) +And output a workday, full of finished slices \ No newline at end of file diff --git a/src/time.odin b/src/time.odin index bdef718..d5fec73 100644 --- a/src/time.odin +++ b/src/time.odin @@ -11,14 +11,14 @@ import "core:sort" // --- STRUCTURES --- // -Weekday : enum{ +Weekday :: enum{ Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, - Sunday + Sunday, } Delta :: struct { @@ -38,8 +38,8 @@ Moment :: struct { Timeblock :: struct { start : Moment, end : Moment, - valuefactor : f32, - price_reason : string, + value : f32, + reason : string, } Workday :: struct { @@ -72,7 +72,7 @@ new_workday :: proc(previous_wrap : Moment, // 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(wrap, {0, 2, 0})), - add(call, {0, 4, 0})), 0, ""} + add(call, {0, 4, 0})), 1, ""} // ^ Minimum 4 hour day ^ sp_length :: 11 @@ -126,43 +126,71 @@ new_workday :: proc(previous_wrap : Moment, j += 1 total_timeblocks = j + // This line is commented out because it shouldn't be needed. //slice.sort_by(blocks[:], lessTimeblock) - for each_block, i in blocks { - fmt.printf("Block %2i: %s\n", i+1, toString(each_block)) - } fmt.println(total_timeblocks) - // 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 + for block, i in &blocks { + if i >= total_timeblocks do break + + //using Weekday + + if lessEq(block.end, splitpoints[0]) do upvalue(&block, 3, "Sleep-breach") // +200% for sleep-breach + if block.start.hours >= 22 do upvalue(&block, 2, "Night") // Work at night, aka. between 22:00 and 06:00 + if (block.end.hours == 6 && block.end.minutes == 0) || block.end.hours <= 5 do upvalue(&block, 2, "Night") // is +100% + if greatEq(block.start, splitpoints[3]) { + upvalue(&block, 1.5, "Overtime") + if getweekday(block.start) == .Saturday do upvalue(&block, 2, "Saturday Overtime") + } + if greatEq(block.start, splitpoints[5]) do upvalue(&block, 2, "Overtime") // End of 3-hour cheap planned overtime + if greatEq(block.start, planned_wrap) && greatEq(block.start, splitpoints[4]) do upvalue(&block, 2, "Overtime") // Unwarned OT + if greatEq(block.start, splitpoints[7]) do upvalue(&block, 3, "Far overtime") // +200% beyond 14th hours is +100% + if getweekday(block.start) == .Saturday do upvalue(&block, 1.5, "Saturday") // Saturdays are +50% + if getweekday(block.start) == .Sunday do upvalue(&block, 2, "Sunday") // Sundays are +100% + + if !(less(call, Moment{0, 7, call.day, call.month, call.year}) && + less(min(add(call, Delta{0,8,0}), wrap), Moment{0, 17, call.day, call.month, call.year} )) { + // This was added for rule 6.11c, but in a world without a defined normal workday, + // that rule is already covered already by 6.11g, so this is empty. + } + + + // Holidays! + if (block.start.day==1) && (block.start.month==1) do upvalue(&block, 2, "New year") + if (block.start.day==1) && (block.start.month==5) do upvalue(&block, 2, "1st of May") + if (block.start.day==17) && (block.start.month==5) do upvalue(&block, 2, "17th of May") + if (block.start.day==25 || block.start.day==26) && block.start.month==12 do upvalue(&block, 2, "Christmas") + easter: Moment = gaussEaster(block.start.year) + if (block.start.day == sub(easter, {0,0,3}).day) && block.start.month == sub(easter, {0,0,3}).month do upvalue(&block, 2, "Maundy Thursday") + if (block.start.day == sub(easter, {0,0,2}).day) && block.start.month == sub(easter, {0,0,2}).month do upvalue(&block, 2, "Good Friday") + if (block.start.day == easter.day) && (block.start.month == easter.month) do upvalue(&block, 2, "Easter") + if (block.start.day == add(easter, {0,0,1}).day) && (block.start.month == add(easter, {0,0,1}).month) do upvalue(&block, 2, "Easter") + if (block.start.day == add(easter, {0,0,39}).day) && (block.start.month == add(easter, {0,0,39}).month) do upvalue(&block, 2, "Feast of the Ascension") + if (block.start.day == add(easter, {0,0,49}).day) && (block.start.month == add(easter, {0,0,49}).month) do upvalue(&block, 2, "Pentecost") + if (block.start.day == add(easter, {0,0,50}).day) && (block.start.month == add(easter, {0,0,50}).month) do upvalue(&block, 2, "Pentecost Monday") + } + + for each_block, i in blocks { + fmt.printf("Block %2i: %s $f: %i%% %s\n", i+1, toString(each_block), int((each_block.value-1)*100), each_block.reason) + } return } +lunch :: proc(workday: ^Workday, lunch_start: Moment, lunch_end: Moment) { + // TODO: Implement lunch! + // + // This version of lunch needs to fix the issue where when lunch spans multiple Timeblocks, + // it would... not act like a proper adult. + // + // |-------|---|-------|----|---------|--------------| + // |--lunch--| + // |-------|---|----| |-------|--------------| + // This ^ must work! +} + windIndividual :: proc(input_moment: ^Moment, minutes: int, hours: int, @@ -236,17 +264,24 @@ timesplit :: proc(block: Timeblock, splitpoint: Moment) -> (first_half: Timebloc return } - first_half = {block.start, splitpoint, block.valuefactor, block.price_reason} - second_half = {splitpoint, block.end, block.valuefactor, block.price_reason} + first_half = {block.start, splitpoint, block.value, block.reason} + second_half = {splitpoint, block.end, block.value, block.reason} return } +upvalue :: proc(input_block: ^Timeblock, value: f32, reason: string) { + block: ^Timeblock = input_block + if value > block.value { + block.value = value + block.reason = reason + } +} + // // --- BASIC OPERATIONS --- // - add :: proc(moment: Moment, delta: Delta) -> (output: Moment) { output = moment wind(&output, delta) @@ -307,6 +342,16 @@ lessTimeblock :: proc(block_a: Timeblock, block_b: Timeblock) -> bool { } less :: proc{lessMoment, lessDelta, lessTimeblock} +lessEqMoment :: proc(moment_a: Moment, moment_b: Moment) -> bool { + return moment_a==moment_b || less(moment_a, moment_b) +} +lessEq :: proc{lessEqMoment} + +greatEqMoment :: proc(moment_a: Moment, moment_b: Moment) -> bool { + return moment_a == moment_b || great(moment_a, moment_b) +} +greatEq :: proc{greatEqMoment} + diff :: proc(moment_a: Moment, moment_b: Moment) -> (acc: Delta) { // TODO: Finish writing this @@ -431,9 +476,14 @@ deltaToString :: proc(delta: Delta) -> (output: string) { output = strings.concatenate(cat_array[:]) return - } +getweekday :: proc(moment: Moment) -> Weekday { + y: int = moment.year + t: []int = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 } + y -= int(moment.month < 3) + return Weekday((y + y / 4 - y / 100 + y / 400 + t[moment.month - 1] + moment.day - 1) % 7) +} momentToString :: proc(moment: Moment) -> (output: string) { using moment @@ -495,6 +545,56 @@ days_in :: proc(month: int, year: int) -> int { return 31; } fmt.printf("You just found month nr: %i. Something is very wrong.\n", month) - assert(month < 13 && month > 0) + fmt.assertf(month < 13 && month > 0, "You tried to get the days in month %i!\n", month) return 30 } + +gaussEaster :: proc(year: int) -> Moment { + // Thanks to Carl Friedrich Gauss for the algorythm + // Thanks rahulhegde97, bansal_rtk_, code_hunt, sanjoy_62, simranarora5sos + // and aashutoshparoha on GeeksForGeeks for the implementation I based this on. + A, B, C, P, Q, M, N, D, E: f64 + easter_month: int = 0 + easter_day: int = 0 + + A = f64(year % 19) + B = f64(year % 4) + C = f64(year % 7) + P = f64(math.floor(f64(year / 100.0))) + + Q = math.floor((13 + 8 * P) / 25.0) + + M = f64(int(15 - Q + P - math.floor(f64(P / 4))) % 30) + + N = f64(int(4 + P - math.floor(P / 4)) % 7) + + D = f64(int(19 * A + M) % 30) + + E = f64(int(2 * B + 4 * C + 6 * D + N) % 7) + + days: int = int(22 + D + E) + easter_day = days + + if (D == 29) && (E == 6) { + // A corner case when D is 29 + easter_month = 4 + easter_day = 19 + } else if (D == 28) && (E == 6) { + // Another corner case, when D is 28 + easter_month = 4 + easter_day = 18 + } else { + // If days > 31, move to April + // April = 4th Month + if (days > 31) { + easter_month = 04 + easter_day = days-31 + } else { + // Otherwise, stay on March + // March = 3rd Month + easter_month = 03 + } + } + + return {0, 0, easter_day, easter_month, year} +} \ No newline at end of file -- cgit v1.2.1