diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0ad02d1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+cmake-build*/
+build/
+out/
+./cmake-build*/
+./build/
+./out/
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml
new file mode 100644
index 0000000..2e24873
--- /dev/null
+++ b/.idea/dictionaries/project.xml
@@ -0,0 +1,7 @@
+
+
+
+ kayda
+
+
+
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
new file mode 100644
index 0000000..5bf27ce
--- /dev/null
+++ b/.idea/editor.xml
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..0b76fe5
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..49e6604
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/schedulizer.iml b/.idea/schedulizer.iml
new file mode 100644
index 0000000..f08604b
--- /dev/null
+++ b/.idea/schedulizer.iml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..309514d
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b4104df..5938ab0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,7 +49,7 @@ macro(blt_add_project name source type)
project(schedulizer)
endmacro()
-project(schedulizer VERSION 0.0.1)
+project(schedulizer VERSION 0.0.2)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
option(ENABLE_UBSAN "Enable the ub sanitizer" OFF)
diff --git a/commit.py b/commit.py
new file mode 100755
index 0000000..b885026
--- /dev/null
+++ b/commit.py
@@ -0,0 +1,318 @@
+#!/usr/bin/python3
+
+import subprocess
+import argparse
+import sys
+import os
+import itertools
+import requests
+import json
+from pathlib import Path
+
+#---------------------------------------
+# CONFIG
+#---------------------------------------
+
+VERSION_BEGIN_STR = " VERSION "
+VERSION_END_STR = ")"
+
+#---------------------------------------
+# DO NOT TOUCH
+#---------------------------------------
+
+USER_HOME = Path.home()
+ENVIRONMENT_DATA_LOCATION = USER_HOME / ".brett_scripts.env"
+
+if sys.platform.startswith("win"):
+ CONFIG_FILE_DIRECTORY = Path(os.getenv('APPDATA') + "\blt")
+ CONFIG_FILE_LOCATION = Path(CONFIG_FILE_DIRECTORY + "\commit_config.json")
+else:
+ XDG_CONFIG_HOME = os.environ.get('XDG_CONFIG_HOME')
+ if XDG_CONFIG_HOME is None:
+ XDG_CONFIG_HOME = USER_HOME / ".config"
+ else:
+ XDG_CONFIG_HOME = Path(XDG_CONFIG_HOME)
+
+ if len(str(XDG_CONFIG_HOME)) == 0:
+ XDG_CONFIG_HOME = USER_HOME
+ CONFIG_FILE_DIRECTORY = XDG_CONFIG_HOME / "blt"
+ CONFIG_FILE_LOCATION = CONFIG_FILE_DIRECTORY / "commit_config.json"
+
+class Config:
+ def __init__(self):
+ # Inline with semantic versioning it doesn't make sense to branch / release on minor
+ self.branch_on_major = True
+ self.branch_on_minor = False
+ self.release_on_major = True
+ self.release_on_minor = True
+ self.main_branch = "main"
+ self.patch_limit = -1
+
+ def toJSON(self):
+ return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4)
+
+ def fromJSON(file):
+ with open(file, "r") as f:
+ j = json.load(f)
+ obj = Config()
+ [setattr(obj, key, val) for key, val in j.items() if hasattr(obj, key)]
+ if obj.branch_on_minor:
+ obj.branch_on_major = True
+ return obj
+
+ def from_file(file):
+ values = {}
+ if (not os.path.exists(file)):
+ return Config()
+
+ with open(file, "r") as f:
+ j = json.load(f)
+ obj = Config()
+ [setattr(obj, key, val) for key, val in j.items() if hasattr(obj, key)]
+ return obj
+
+ def save_to_file(self, file):
+ dir_index = str(file).rfind("/")
+ dir = str(file)[:dir_index]
+ if not os.path.exists(dir):
+ print(f"Creating config directory {dir}")
+ os.makedirs(dir)
+ with open(file, "w") as f:
+ json.dump(self, f, default=lambda o: o.__dict__, sort_keys=True, indent=4)
+
+
+class EnvData:
+ def __init__(self, github_username = '', github_token = ''):
+ self.github_token = github_token
+ self.github_username = github_username
+
+ def get_env_from_file(file):
+ f = open(file, "rt")
+ values = {}
+ for line in f:
+ if line.startswith("export"):
+ content = line.split("=")
+ for idx, c in enumerate(content):
+ content[idx] = c.replace("export", "").strip()
+ values[content[0]] = content[1].replace("\"", "").replace("'", "")
+ try:
+ github_token = values["github_token"]
+ except Exception:
+ print("Failed to parse github token!")
+ try:
+ github_username = values["github_username"]
+ except:
+ print("Failed to parse github username! Assuming you are me!")
+ github_username = "Tri11Paragon"
+ return EnvData(github_username=github_username, github_token=github_token)
+
+def open_process(command, print_out = True):
+ process = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+ stdout, stderr = process.communicate()
+ exit_code = process.wait()
+ str_out = stdout.decode('utf8')
+ str_err = stderr.decode('utf8')
+ if print_out and len(str_out) > 0:
+ print(str_out, end='')
+ if print_out and len(str_err) > 0:
+ print(str_err, end='')
+ #print(stdout, stderr, exit_code)
+ return (stdout, stderr, exit_code)
+
+def load_cmake():
+ cmake_file = open("CMakeLists.txt", 'r')
+ cmake_text = cmake_file.read()
+ cmake_file.close()
+ return cmake_text
+
+def write_cmake(cmake_text):
+ cmake_file = open("CMakeLists.txt", 'w')
+ cmake_file.write(cmake_text)
+ cmake_file.close()
+
+def get_version(cmake_text):
+ begin = cmake_text.find(VERSION_BEGIN_STR) + len(VERSION_BEGIN_STR)
+ end = cmake_text.find(VERSION_END_STR, begin)
+ return (cmake_text[begin:end], begin, end)
+
+def split_version(cmake_text):
+ version, begin, end = get_version(cmake_text)
+ version_parts = version.split('.')
+ return (version_parts, begin, end)
+
+def recombine(cmake_text, version_parts, begin, end):
+ constructed_version = version_parts[0] + '.' + version_parts[1] + '.' + version_parts[2]
+ constructed_text_begin = cmake_text[0:begin]
+ constructed_text_end = cmake_text[end::]
+ return constructed_text_begin + constructed_version + constructed_text_end
+
+def inc_major(cmake_text):
+ version_parts, begin, end = split_version(cmake_text)
+ version_parts[0] = str(int(version_parts[0]) + 1)
+ version_parts[1] = '0'
+ version_parts[2] = '0'
+ return recombine(cmake_text, version_parts, begin, end)
+
+def inc_minor(cmake_text):
+ version_parts, begin, end = split_version(cmake_text)
+ version_parts[1] = str(int(version_parts[1]) + 1)
+ version_parts[2] = '0'
+ return recombine(cmake_text, version_parts, begin, end)
+
+def inc_patch(config: Config, cmake_text):
+ version_parts, begin, end = split_version(cmake_text)
+ if config.patch_limit > 0 and int(version_parts[2]) + 1 >= config.patch_limit:
+ return inc_minor(cmake_text)
+ version_parts[2] = str(int(version_parts[2]) + 1)
+ return recombine(cmake_text, version_parts, begin, end)
+
+def make_branch(config: Config, name):
+ print(f"Making new branch {name}")
+ subprocess.call(["git", "checkout", "-b", name])
+ subprocess.call(["git", "merge", config.main_branch])
+ subprocess.call(["git", "checkout", config.main_branch])
+
+def sync_branch(config: Config, version_parts, args):
+ if config.branch_on_major:
+ # Branch will be created.
+ if args.minor:
+ return;
+
+def make_release(env: EnvData, name):
+ print(f"Making new release {name}")
+ repos_v = open_process(["git", "remote", "-v"])[0].splitlines()
+ urls = []
+ for line in repos_v:
+ origin = ''.join(itertools.takewhile(str.isalpha, line.decode('utf8')))
+ urls.append(open_process(["git", "remote", "get-url", origin], False)[0].decode('utf8').replace("\n", "").replace(".git", "").replace("https://github.com/", "https://api.github.com/repos/") + "/releases")
+ urls = set(urls)
+ data = {
+ 'tag_name': name,
+ 'name': name,
+ 'body': "Automated Release '" + name + "'",
+ 'draft': False,
+ 'prerelease': False
+ }
+ headers = {
+ 'Authorization': f'Bearer {env.github_token}',
+ 'Accept': 'application/vnd.github+json',
+ 'X-GitHub-Api-Version': '2022-11-28'
+ }
+ for url in urls:
+ if not "github" in url:
+ return
+ response = requests.post(url, headers=headers, data=json.dumps(data))
+ if response.status_code == 201:
+ print('Release created successfully!')
+ release_data = response.json()
+ print(f"Release URL: {release_data['html_url']}")
+ else:
+ print(f"Failed to create release: {response.status_code}")
+ print(response.json())
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ prog="Commit Helper",
+ description="Help you make pretty commits :3")
+
+ parser.add_argument("action", nargs='?', default=None)
+ parser.add_argument("-p", "--patch", action='store_true', default=False, required=False)
+ parser.add_argument("-m", "--minor", action='store_true', default=False, required=False)
+ parser.add_argument("-M", "--major", action='store_true', default=False, required=False)
+ parser.add_argument('-e', "--env", help="environment file", required=False, default=None)
+ parser.add_argument('-c', "--config", help="config file", required=False, default=None)
+ parser.add_argument("--create-default-config", action="store_true", default=False, required=False)
+ parser.add_argument("--no-release", action="store_true", default=False, required=False)
+ parser.add_argument("--no-branch", action="store_true", default=False, required=False)
+
+ args = parser.parse_args()
+
+ if args.create_default_config:
+ config = Config()
+ config.save_to_file(args.config if args.config is not None else CONFIG_FILE_LOCATION)
+ return
+
+ if args.env is not None:
+ env = EnvData.get_env_from_file(args.e)
+ else:
+ env = EnvData.get_env_from_file(ENVIRONMENT_DATA_LOCATION)
+
+ if args.config is not None:
+ config = Config.from_file(args.config)
+ else:
+ config = Config.from_file(CONFIG_FILE_LOCATION)
+
+ cmake_text = load_cmake()
+ cmake_version = get_version(cmake_text)[0]
+ print(f"Current Version: {cmake_version}")
+
+ if not (args.patch or args.minor or args.major):
+ try:
+ if args.action is not None:
+ type = args.action
+ else:
+ type = input("What kind of commit is this ((M)ajor, (m)inor, (p)atch)? ")
+
+ if type.startswith('M'):
+ args.major = True
+ elif type.startswith('m'):
+ args.minor = True
+ elif type.startswith('p') or type.startswith('P') or len(type) == 0:
+ args.patch = True
+ except KeyboardInterrupt:
+ print("\nCancelling!")
+ return
+
+ if args.major:
+ print("Selected major")
+ write_cmake(inc_major(cmake_text))
+ elif args.minor:
+ print("Selected minor")
+ write_cmake(inc_minor(cmake_text))
+ elif args.patch:
+ print("Selected patch")
+ write_cmake(inc_patch(config, cmake_text))
+
+ subprocess.call(["git", "add", "*"])
+ subprocess.call(["git", "commit"])
+
+ cmake_text = load_cmake()
+ version_parts = split_version(cmake_text)[0]
+ if args.major:
+ if config.branch_on_major:
+ if not args.no_branch:
+ make_branch(config, "v" + str(version_parts[0]))
+
+ if args.minor:
+ if config.branch_on_minor:
+ if not args.no_branch:
+ make_branch(config, "v" + str(version_parts[0]) + "." + str(version_parts[1]))
+ elif config.branch_on_major:
+ subprocess.call(["git", "checkout", "v" + str(version_parts[0])])
+ subprocess.call(["git", "rebase", config.main_branch])
+ subprocess.call(["git", "checkout", config.main_branch])
+
+ if args.patch:
+ if config.branch_on_minor:
+ subprocess.call(["git", "checkout", "v" + str(version_parts[0]) + "." + str(version_parts[1])])
+ subprocess.call(["git", "rebase", config.main_branch])
+ subprocess.call(["git", "checkout", config.main_branch])
+ elif config.branch_on_major:
+ subprocess.call(["git", "checkout", "v" + str(version_parts[0])])
+ subprocess.call(["git", "rebase", config.main_branch])
+ subprocess.call(["git", "checkout", config.main_branch])
+
+ sync_branch(config=config, version_parts=version_parts, args=args)
+
+ subprocess.call(["sh", "-c", "git remote | xargs -L1 git push --all"])
+
+ if args.major:
+ if not args.no_release and config.release_on_major:
+ make_release(env, "v" + str(version_parts[0]))
+ if args.minor:
+ if not args.no_release and config.release_on_minor:
+ make_release(env, "v" + str(version_parts[0]) + "." + str(version_parts[1]))
+
+if __name__ == "__main__":
+ main()
diff --git a/src/main.cpp b/src/main.cpp
index 9bcc342..ce18295 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6,38 +6,90 @@
constexpr blt::i32 SHIFTS_PER_WEEK = 5;
constexpr blt::i32 POPULATION_SIZE = 100;
+blt::random::random_t& get_random()
+{
+ thread_local blt::random::random_t random{691};
+ return random;
+}
+
enum class shift_t : blt::u8
{
- NONE, AMs, PMs
+ OFF, AMs, PMs
};
-enum class weekday_t : blt::u8
+struct weekday_t
{
- SATURDAY,
- SUNDAY,
- MONDAY,
- TUESDAY,
- WEDNESDAY,
- THURSDAY,
- FRIDAY
-};
-
-bool days_require_pair(const weekday_t day)
-{
- switch (day)
+ enum value_t : blt::u8
{
- case weekday_t::SATURDAY:
- case weekday_t::SUNDAY:
- return true;
- case weekday_t::MONDAY:
- case weekday_t::TUESDAY:
- case weekday_t::WEDNESDAY:
- case weekday_t::THURSDAY:
- case weekday_t::FRIDAY:
- return false;
+ SATURDAY,
+ SUNDAY,
+ MONDAY,
+ TUESDAY,
+ WEDNESDAY,
+ THURSDAY,
+ FRIDAY
+ };
+
+ weekday_t() = default;
+
+ constexpr weekday_t(const value_t value) : value(value) // NOLINT
+ {}
+
+ constexpr operator value_t() const // NOLINT
+ {
+ return value;
}
- BLT_UNREACHABLE;
-}
+
+ explicit operator bool() const = delete;
+
+ constexpr bool operator==(const weekday_t a) const
+ {
+ return value == a.value;
+ }
+
+ constexpr bool operator!=(const weekday_t a) const
+ {
+ return value != a.value;
+ }
+
+ [[nodiscard]] bool is_weekend() const
+ {
+ switch (value)
+ {
+ case SATURDAY:
+ case SUNDAY:
+ return true;
+ case MONDAY:
+ case TUESDAY:
+ case WEDNESDAY:
+ case THURSDAY:
+ case FRIDAY:
+ return false;
+ }
+ BLT_UNREACHABLE;
+ }
+
+ [[nodiscard]] std::vector& available_shifts() const
+ {
+ thread_local std::vector shifts;
+
+ shifts.clear();
+ if (is_weekend())
+ {
+ constexpr std::array weekend_shifts = {shift_t::AMs, shift_t::PMs, shift_t::AMs, shift_t::PMs};
+ shifts.insert(shifts.end(), weekend_shifts.begin(), weekend_shifts.end());
+ return shifts;
+ } else
+ {
+
+ }
+
+ return shifts;
+ }
+
+private:
+ value_t value;
+};
struct placement_t
{
@@ -50,22 +102,15 @@ struct placement_t
struct day_t
{
- placement_t brett{shift_t::PMs, true};
+ placement_t brett;
placement_t kayda;
placement_t tim;
placement_t braeden;
weekday_t day;
- [[nodiscard]] bool days_require_pair() const
- {
- return ::days_require_pair(day);
- }
-
- day_t(const placement_t& kayda, const placement_t& tim, const placement_t& braeden, const weekday_t day) : kayda{kayda},
- tim{tim},
- braeden{braeden},
- day{day}
+ day_t(const placement_t& brett, const placement_t& kayda, const placement_t& tim, const placement_t& braeden,
+ const weekday_t day): brett{brett}, kayda{kayda}, tim{tim}, braeden{braeden}, day{day}
{}
};
@@ -82,26 +127,15 @@ struct week_t
class shift_solver_t
{
public:
- static placement_t
-
static day_t generate_random_day(const weekday_t day)
{
auto& random = get_random();
std::array placements{};
if (days_require_pair(day))
- {
-
- } else
- {
-
- }
+ {} else
+ {}
}
- static blt::random::random_t& get_random()
- {
- thread_local blt::random::random_t random{691};
- return random;
- }
private:
};