From fa6b030d981db5e0c88f62baaf27673bf8d74024 Mon Sep 17 00:00:00 2001 From: Awstin Date: Fri, 17 May 2024 14:55:30 -0400 Subject: [PATCH] Projects now using article template everywhere --- src/html/blog.rs | 19 +- src/html/mod.rs | 8 + src/html/projects.rs | 89 ++++---- src/html/root.rs | 16 +- templates/Article.html | 12 -- templates/article.html | 12 ++ templates/blog_footer.html | 1 + templates/home.html | 7 +- templates/projects.html | 7 +- templates/projects/ArchServer.html | 282 -------------------------- templates/projects/EdgeDetection.html | 103 ---------- templates/projects/Tak.html | 122 ----------- 12 files changed, 98 insertions(+), 580 deletions(-) delete mode 100755 templates/Article.html create mode 100755 templates/article.html create mode 100644 templates/blog_footer.html delete mode 100755 templates/projects/ArchServer.html delete mode 100755 templates/projects/EdgeDetection.html delete mode 100755 templates/projects/Tak.html diff --git a/src/html/blog.rs b/src/html/blog.rs index c504eb7..50b7b98 100644 --- a/src/html/blog.rs +++ b/src/html/blog.rs @@ -9,7 +9,7 @@ use core::panic; use sqlx::PgPool; use std::{collections::HashMap, error::Error}; -use super::{root::AppState, HtmlTemplate, NavBar}; +use super::{root::AppState, ArticleTemplate, HtmlTemplate, NavBar}; pub fn get_router() -> Router { Router::new() @@ -37,12 +37,10 @@ struct BlogTemplate { } #[derive(Template)] -#[template(path = "Article.html")] -struct ArticleTemplate { - active_navbar: &'static str, - next: String, +#[template(path = "blog_footer.html")] +struct BlogFooterTemplate { previous: String, - content: String, + next: String, } async fn article( @@ -55,9 +53,7 @@ async fn article( Ok(a) => *a, Err(_) => panic!("Not an article at all!!!!"), }; - let template = ArticleTemplate { - active_navbar: NavBar::BLOG, - content: article.content, + let footer = BlogFooterTemplate { previous: match article.previous { Some(a) => a, None => "".to_string(), @@ -67,6 +63,11 @@ async fn article( None => "".to_string(), }, }; + let template = ArticleTemplate { + active_navbar: NavBar::BLOG, + content: article.content, + footer: footer.to_string(), + }; HtmlTemplate(template) } diff --git a/src/html/mod.rs b/src/html/mod.rs index 411a34f..a0872d1 100644 --- a/src/html/mod.rs +++ b/src/html/mod.rs @@ -43,3 +43,11 @@ impl NavBar { pub const ABOUT: &'static str = "about"; pub const CONTACT: &'static str = "contact"; } + +#[derive(Template)] +#[template(path = "article.html")] +pub struct ArticleTemplate { + active_navbar: &'static str, + footer: String, + content: String, +} diff --git a/src/html/projects.rs b/src/html/projects.rs index fa93c1f..7a9cc4c 100644 --- a/src/html/projects.rs +++ b/src/html/projects.rs @@ -1,20 +1,27 @@ +use achubb_database::data::{PsqlData, project::Project}; +use axum::extract::{Extension, Path}; use askama::Template; use axum::response::IntoResponse; use axum::{routing::get, Router}; +use std::{ error::Error, collections::HashMap}; +use sqlx::PgPool; -use super::{HtmlTemplate, NavBar}; +use super::{root::AppState, ArticleTemplate, HtmlTemplate, NavBar}; pub fn get_router() -> Router { Router::new() .route("/", get(projects)) - .route("/archserver", get(archserver)) - .route("/tak", get(tak)) - .route("/ed", get(ed)) + .route("/:project", get(project)) } -pub async fn projects() -> impl IntoResponse { +pub async fn projects(state: Extension) -> impl IntoResponse { + let db_pool = &state.db; + let list: Vec = get_projects_as_links_list(db_pool) + .await + .expect("couldn't get projects"); let template = ProjectsTemplate { active_navbar: NavBar::PROJECTS, + project_list: list.join("\n"), }; HtmlTemplate(template) } @@ -23,43 +30,51 @@ pub async fn projects() -> impl IntoResponse { #[template(path = "projects.html")] struct ProjectsTemplate { active_navbar: &'static str, + project_list: String, } -async fn archserver() -> impl IntoResponse { - let template = ArchServerTemplate { - active_navbar: NavBar::PROJECTS - }; +async fn project( + state: Extension, + Path(params): Path>, +) -> impl IntoResponse { + let db_pool = &state.db; + let project_id: &String = params.get("project").unwrap(); + let project: Project = match Project::read_by_reference(db_pool, project_id).await { + Ok(a) => *a, + Err(_) => panic!("Not an article at all!!!!"), + }; + let footer: &str = "Back to Projects"; + let template = ArticleTemplate { + active_navbar: NavBar::PROJECTS, + content: project.content.expect("Should have had content if it got this far"), + footer: footer.to_string(), + }; HtmlTemplate(template) } -#[derive(Template)] -#[template(path = "projects/ArchServer.html")] -struct ArchServerTemplate { - active_navbar: &'static str, -} +pub async fn get_projects_as_links_list(pool: &PgPool) -> Result, Box> { + let mut projects: Vec = match Project::read_all(pool).await { + Ok(a) => a.iter().map(|x| *x.clone()).collect(), + Err(_) => panic!("Not a project at all!!!!"), + }; -async fn tak() -> impl IntoResponse { - let template = TakTemplate { - active_navbar: NavBar::PROJECTS - }; - HtmlTemplate(template) -} + projects.sort_by(|a, b| b.date.cmp(&a.date)); -#[derive(Template)] -#[template(path = "projects/Tak.html")] -struct TakTemplate { - active_navbar: &'static str, -} - -async fn ed() -> impl IntoResponse { - let template = EdgeDetectionTemplate { - active_navbar: NavBar::PROJECTS - }; - HtmlTemplate(template) -} - -#[derive(Template)] -#[template(path = "projects/EdgeDetection.html")] -struct EdgeDetectionTemplate { - active_navbar: &'static str, + let list: Vec = projects + .iter() + .map(|project| { + if project.blog { + format!( + "
  • {}
  • ", + project.reference, project.title + ) + } else { + format!( + "
  • {}
  • ", + project.reference, project.title + ) + } + }) + .collect(); + Ok(list) } diff --git a/src/html/root.rs b/src/html/root.rs index 7e10d88..1c36976 100644 --- a/src/html/root.rs +++ b/src/html/root.rs @@ -8,7 +8,7 @@ use axum::{ use sqlx::PgPool; use tower_http::services::ServeDir; -use super::blog::get_articles_as_links_list; +use super::{blog::get_articles_as_links_list, projects::get_projects_as_links_list}; #[derive(Clone)] pub struct AppState { @@ -39,15 +39,22 @@ pub fn get_router(pool: PgPool) -> Router { async fn home(state: Extension) -> impl IntoResponse { let db_pool = &state.db; - let list: Vec = get_articles_as_links_list(db_pool) + let article_list: Vec = get_articles_as_links_list(db_pool) .await .expect("couldn't get articles"); - let (head, _) = list.split_at(5); + let (article_head, _) = article_list.split_at(5); + + let project_list: Vec = get_projects_as_links_list(db_pool) + .await + .expect("Couldn't get projects"); + + let (project_head, _) = project_list.split_at(5); let template = HomeTemplate { active_navbar: NavBar::HOME, - recent_blogs: head.join("\n"), + recent_blogs: article_head.join("\n"), + recent_projects: project_head.join("\n"), }; HtmlTemplate(template) } @@ -57,6 +64,7 @@ async fn home(state: Extension) -> impl IntoResponse { struct HomeTemplate { active_navbar: &'static str, recent_blogs: String, + recent_projects: String, } async fn now() -> impl IntoResponse { diff --git a/templates/Article.html b/templates/Article.html deleted file mode 100755 index d7493c7..0000000 --- a/templates/Article.html +++ /dev/null @@ -1,12 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} -
    - {{content|safe}} -
    - -
    - {%if previous != ""%}previous{%endif%}{%if previous != "" && next != ""%} / {%endif%}{%if next != ""%}next{%endif%} -
    -{% endblock %} diff --git a/templates/article.html b/templates/article.html new file mode 100755 index 0000000..73ba9ae --- /dev/null +++ b/templates/article.html @@ -0,0 +1,12 @@ + +{% extends "base.html" %} + +{% block content %} +
    + {{content|safe}} +
    + +
    + {{footer|safe}} +
    +{% endblock %} diff --git a/templates/blog_footer.html b/templates/blog_footer.html new file mode 100644 index 0000000..f9434de --- /dev/null +++ b/templates/blog_footer.html @@ -0,0 +1 @@ +{%if previous != ""%}previous{%endif%}{%if previous != "" && next != ""%} / {%endif%}{%if next != ""%}next{%endif%} diff --git a/templates/home.html b/templates/home.html index 35713e2..43bd0a1 100644 --- a/templates/home.html +++ b/templates/home.html @@ -44,12 +44,9 @@

    Projects

    -

    Featured

    +

    Recent

    diff --git a/templates/projects.html b/templates/projects.html index ffecc5d..c74313d 100644 --- a/templates/projects.html +++ b/templates/projects.html @@ -5,12 +5,7 @@

    diff --git a/templates/projects/ArchServer.html b/templates/projects/ArchServer.html deleted file mode 100755 index 257d52a..0000000 --- a/templates/projects/ArchServer.html +++ /dev/null @@ -1,282 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} -
    -

    - Arch Server -

    -

    - The majority of the process to set up this server was followed from here. - Here I will outline where I deviated and use workarounds to make all this work on Arch Linux. -

    - -
    -

    Register a Domain -> Create SSH Key

    -
      -
    • No changes
    • -
    - -
    -

    Create Your Server

    -
      -
    • Step 6: Select Arch as operating system istead of OpenBSD
    • -
    - -
    -

    Attach Storage -> Point Your Domain Here

    -
      -
    • No changes
    • -
    - -
    -

    SSH Into Root

    -
      -
    • Step 9: command to update packages on Arch is pacman -Syu instead of syspatch
    • -
    - -
    -

    Create Your Username

    -
      -
    • Step 2-16: useradd -m <UserName> will automatically fill out all the defaults
    • -
    • Step 17-20: passwd <UserName> to set the users password.
    • -
    • Step 21: usermod -aG wheel,wheelnpw <UserName> to allow executing as root without password
    • -
    - -
    -

    Another User?

    -
      -
    • No extra users added
    • -
    - -
    -

    Secure Your login

    -
      -
    • Step 2: replace doas in command with sudo
    • -
    • Step 5: replace rcctl in command with systemctl
    • -
    - -
    -

    Format Storage

    -

    - Here I deviated much more from the steps so I will write out the steps used without referring to Derek's original document. -

    -
    -
      -
    • ssh <UserName>@yourdomain.name
    • -
    • sudo su
    • -
    • fdisk -l will list the disks available, look for the one with the size of the blockstorage you added to your server. -
        -
      • Likely /dev/vdb
      • -
      -
    • -
    • cryptsetup -y -v --type luks2 luksFormat /dev/vdb -
        -
      • Will ask for storage name, I used blockstorage
      • -
      • Enter your passphrase when asked and confirm it. Make sure you note this down somewhere, you will not be able to access your storage without it.
      • -
      -
    • -
    • dd if=/dev/zero of=/dev/mapper/blockstorage status=progress -
        -
      • This will write all 0s to the memory of the blockstorage and ensure that the data seen from the outside is random.
      • -
      • It will take a long time to complete. status=progress will show you the progress and you will be able to tell when it has completed.
      • -
      -
    • -
    • mkfs.ext4 /dev/mapper/blockstorage will create the filesystem on the disk
    • -
    • mkdir /mnt/blockstorage to create the mountpoint
    • -
    • chown /mnt/blockstorage <UserName>:<UserName> to set ownership to your user
    • -
    • Basic commands to manually manage the filesystem -
        -
      • To mount the filesystem sudo mount /dev/mapper/blockstorage /mnt/blockstorage
      • -
      • To unlock the filesystem sudo cryptsetup luksOpen /dev/vdb blockstorage
      • -
      • To lock the filesystem sudo cryptsetup luksClose blockstorage
      • -
      • To unmount filesystem sudo umount /mnt/blockstorage
      • -
      -
    • -
    -
    -

    - We don't need to deal with these complex commands each time. We can use aliasing to substitute much simpler commands to remember instead. -

    -
    -
      -
    • echo "alias mntblk='sudo mount /dev/mapper/blockstorage /mnt/blockstorage' >> .bashrc" will set the mounting command to mntblk
    • -
    • echo "alias unlkblk='sudo cryptsetup luksOpen /dev/vdb blockstorage' >> .bashrc" will set the unlocking command to unlkblk
    • -
    • echo "alias lkblk='sudo cryptsetup luksClose blockstorage' >> .bashrc" will set the locking command to lkblk
    • -
    • echo "alias umntblk='sudo umount /mnt/blockstorage' >> .bashrc" will set the unmounting command to umntblk
    • -
    -
    -

    - This will still need 2 commands to mount, and 2 more to unmount. - I like having these aliased separately just in case I want to run one without the other for some reason. - To bring this to 1 command for each I will make 2 more aliases using the ones I just created. - You could make each of these 2 using the expanded commands above, but I find this way simpler to read. -

    -
    -
      -
    • echo "alias m='mntblk && unlkblk' >> .bashrc" will make m mount the system and unlock it
    • -
    • echo "alias m-x='lkblk && umntblk' >> .bashrc" will make m-x lock the filesystem and unmount it
    • -
    • log out and log back in to be able to use these new aliases or run source .bashrc
    • -
    - -
    -

    Use Your Storage

    -
      -
    • No changes
    • -
    - -
    -

    FreeFileSync

    -
      -
    • I did not use this
    • -
    - -
    -

    Verify and Unmount

    -
      -
    • No changes
    • -
    - -
    -

    Web Server

    -

    - Ports are not open externally by defaul on Arch. - sudo ufw 80 will open the port for the web server. - Do not open unnecessary ports. - Arch is fully capable of hosting an apache server like is used in the guide. - I prefer Nginx and so used that. - The configuration is a little more complex than apache but I am more familiar with it and it works well for single site hosting. -

    -
    -
      -
    • install with sudo pacman -S nginx
    • -
    • enable with sudo systemctl enable nginx.service
    • -
    • start with sudo systemctl start nginx.service
    • -
    -
    -

    - When I was testing I was serving static content from /srv/http/domain.com/public_html. - In the /etc/nginx/nginx.conf file in the http block I added: -

    -
    
    -    server {:
    -        server_name yourdomain www.yourdomain
    -        location / {:
    -            root /srv/http/yourdomain/public_html
    -        }
    -    }
    -    
    -
    -

    - Run nginx -s reload to reload the configuration file. -

    -

    - Then the html files to be served can be put in whatever folder specified in root and will be served from there. - In the end I have my website built and served using SvelteKit and Nginx is working as a proxy for that program. - I will go into Svelte in the future. -

    -

    - To have a secure webserver (https) I need a certificate. -

    -
      -
    • sudo pacman -S certbot certbot-nginx to install the certbot package and nginx plugin
    • -
    • sudo certbot --nginx will scan for nginx sites and create certificates
    • -
    • sudo certbot renew will renew all certificates on the machine managed by certbot.
    • -
    -
    -

    - To run this renewal periodically I made certref file in bin containing: -

    -
    
    -    #!/bin/bash
    -    sudo certbot renew
    -    
    -

    - Set permissions to 700 with sudo chmod 700 bin/certref, then added a cronjob to run the refresh once a month. -

    - -
    -

    Simple Website

    -

    - Can use exactly what is in the guide here. - Just make sure the files are located in the root you specified in the Nginx configuration above. -

    - -
    -

    File Sharing With Pub

    -

    - Did not set this up with Nginx yet. - If I do will update this. -

    - -
    -

    Calendar and Contacts

    -

    - This is very similar for both Arch and OpenBSD so will list changes - Arch does not have htpasswd by default. - Install with sudo pacman -S apache then continue. -

    -
    -
      -
    • Step 2: sudo su
    • -
    • Step 3: pacman -S radicale
    • -
    • Step 9: systemctl enable radicale.service
    • -
    • Step 10: systemctl start radicale.service
    • -
    - -
    -

    Android and Iphone + Test

    -
      -
    • No changes
    • -
    - -
    -

    Backups

    -
      -
    • Step 3: replace '/sh' with '/bash' and 'doas' with 'sudo' in the echo command.
    • -
    - -
    -

    Email

    -

    - I decided to use an external email provider (FastMail) instead of setting up the email verification myself. - This was before the explanation for this was published in this article. - May try again in the future but for now I am happy with my own email for a few dollars a month that I can move to a different provider if needed -

    - -
    -

    Git

    -

    - I use git extensively in my coding projects, configurations, and notes so I wanted to be able to host my own git server. -

    -
    -
      -
    • assume root with sudo su
    • -
    • install with pacman -S git
    • -
    • in /etc/passwd file set the home directory for the git user to /srv/git
    • -
    • created .ssh folder in /srv/git and added an ssh key like above to log into the server
    • -
    • enabled with systemctl enable git-daemon.socket
    • -
    • started with systemctl start git-daemon.socket
    • -
    -
    -

    - To make a new project I navigate to /srv/git and make a new project with git -bare init <projectName>.git. - Set ownership to git user with chown <projectName>.git git:git. - To set the remote origin for a project that is already initialized git remote add origin git@url:/srv/git/<projectName> - To clone a project from remote to local git clone git://url/<projectName> localName -

    - -
    -

    Evolving

    -

    - This project is slowly but constantly evolving. - The above state is how it is running currently and that may change. - I will endeavor to update it when things do. - Slowly grow from something mostly taken from Derek's guide (thank you so much for that) to something uniquely my own and serving the my exact needs. -

    -
    - - -{% endblock %} diff --git a/templates/projects/EdgeDetection.html b/templates/projects/EdgeDetection.html deleted file mode 100755 index 5ac37f7..0000000 --- a/templates/projects/EdgeDetection.html +++ /dev/null @@ -1,103 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} -
    -

    - Edge Detection -

    -

    - This was a project that I did with two classmates for a parallel programming class. - Source code here. - The programming was done in C except for a python script for image pre-processing (it was just much faster to write). -

    -

    - Edge detection is usually done with convolutions. - A convolution is a matrix of values (kernel) that detects the difference in colour density of pixels in an image. - The kernel is overlayed on the pixel values in the image and each pixel value is multiplied by the value in the kernel above it. - All the resulting values are added together into a single value. - The design of the kernel dictates what sort of feature will be detected. - A large resulting value indicates the presence of the desired feature. -

    -

    - This is the sample input image we used for some of the testing. -

    -
    - Input -
    Input image
    -
    -

    - This image has a lot of both horizontal and vertical lines for detection. - The image gets converted into grayscale during preprocessing for ease of use. -

    -

    - We used Sobel Kernels as opposed to designing our own. - These are very commonly used for edge detection. - The larger the dimensions of the kernel the larger an area of the image it inspect. - A smaller kernel will only find very well defined edges. - 3×3 was the smallest kernel that we used. -

    -
    - Horizontal line 3x3 kernel - Vertical line 3x3 kernel -
    3x3 Soebel Kernels (left:Horizontal, right:Vertical)
    -
    -

    - These kernels found the very defined edges on the sides of the trees and branches. - This includes their reflections in the water below, though the reflection did dampen the edges a fair bit. -

    -
    - 3x3 detection output -
    3x3 Edge detection output
    -
    -

    - As you can see it picked up almost nothing from the sky in the image. - We also tested kernels of size 5×5, 7×7, and 9×9. - Here are the 9×9 kernels we used. -

    -
    - Horizontal line 9x9 kernel - Horizontal line 9x9 kernel -
    9x9 Soebel Kernels (left:Horizontal, right:Vertical)
    -
    -

    - These large kernels will detect differences in gradient across a much larger portion of the image at once. - This results in a much more textured image. -

    -
    - 9x9 detection output -
    9x9 Edge detection output
    -
    -

    - As you can see the trees are almost a uniform mass of edges. - The reflections less so but still very dense. - It did also manage to pick up the texture of the clouds. - Different kernels are good at detecting different types of edges. - A smaller kernel will be much better for sharp edges and will ignore the rest. - A larger kernel will get much less well defined edges. -

    -

    - There is also a large difference in the speed of the program, a larger kernel has significantly more operations that need to be performed. - After the application of the convolutions the rest of the project was parallelizing this process in an effort to speed it up. - We used both Message Passing Interface (MPI) and CUDA for parallelization in order to compare them. - MPI uses the CPU and so is limited by the number of cores the CPU of the device has. - CUDA uses the graphics card so has many more cores to work with but has more overhead. -

    -
    - Convolution time graph -
    -

    - Unless you have access to a large number of CPU cores CUDA is almost always better. - For a general application as long as you have access to a graphics card CUDA is the way to go. -

    -

    - This project was very enlightening as to how the underlying processes for image recognition work. - I have done a little bit of machine learning with convolutional neural networks, but for most libraries this portion is already done for you. - Understanding what is going on under the hood was very interesting. -

    -
    - - -{% endblock %} diff --git a/templates/projects/Tak.html b/templates/projects/Tak.html deleted file mode 100755 index 81c8d10..0000000 --- a/templates/projects/Tak.html +++ /dev/null @@ -1,122 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} -
    -

    - Tak -

    -

    - One of my favourite fantasy book series is the Kingkiller Chronicles by Patrick Rothfuss. - In the second book The Wise Man’s Fear there is a board game that they play that sounded fascinating. - I have always like abstract strategy games and this was his worlds “Chess”; the game that had been around forever, simple rules but deep strategy etc. -

    -

    - I was so excited when I found out that it was being made into a full game by Cheapass Games. - The rules are available for free on their website. - I bought myself a copy and it is a lot of fun. - They did an amazing job getting the feel of the game right and it is now for sale on WorldBuilders Market. - Proceeds from WorldBuilders go to charity so if you try out the game and enjoy it please consider buying a copy. -

    -

    - As a project I made a digital copy of the game. - Source code here. - It is written in python 3 and the only extra library it needs to run is pygame. - If you want to run the tests you will need pytest as well. -

    -

    - Here is the program interface. -

    -
    - Home Board -
    Tak Program
    -
    -

    - The game was a very interesting challenge to implement for a few reasons. -

    -

    - The board size is flexible. - It ranges from 3×3 to 8×8 (skipping 7×7 but I am not quite sure why). - The rules for victory are the same regardless of board size but the number of pieces each player has access to changes. - The board had to be housed in a flexible object that would also dictate the number of pieces. -

    -

    - While there are only two types of pieces (regular and capstones), the standard pieces can be played in 2 different orientations. - Flat placement is the default. - It contributes to win conditions and can make stacks. - Placing a normal piece as a wall blocks other pieces but does not contribute to your win condition and can only be at the top of a stack. - The capstone has the best characteristics of both and can squish walls to flat if it lands on them alone. -

    -
    - Pieces -
    Tak pieces (left to right: flat, wall, capstone)
    -
    -

    - Rather than make an object for each piece I built the piece specific rules into the move action. - The move is the same at its core for each piece and the only difference is the interaction when one piece lands on top of another. - Storing the pieces as integers in a 2d array makes this far simpler. - As the interactions depend on both pieces, using an integer makes that comparison very easy. -

    -

    - The game has an interesting 3 dimensional component. - The pieces can be stacked on top of one another and a stack can move as far as the stack is high (in a straight line controlled by the player whose piece is at the top) dropping one piece at a minimum on each square along the way. - This means that each square in the 2d array needs to contain a stack where new pieces are added to the top. - When pieces are lifted off a stack your hand acts like a second stack. - Pieces are popped off the top of the square and added to the stack in your hand. - Then they are dropped from the “bottom” of this inverted stack as you move. -

    -
    - Move view - Drop view -
    -

    - The comparison needs to be done from the “bottom” of the inverted stack in your hand to the top of the stack that the piece will land on. -

    -

    - That is the main challenge of the game play. - The victory conditions were also challenging. - The game has 3 different ways that it can end and 2 possible ways to calculate who won. - -

    -

    - The game ends when one player builds a connected line of pieces from one of the sides of the board to the opposite side. - The line does not need to be straight but squares do not connect on the diagonal. - The victor in this case is the player who has the “road”. - If a move creates a road for both players the active player wins. -

    -
    - Victory -
    Victory!
    -
    -

    - The game also ends if either player runs out of pieces or there are no more open spaces on the board. - In that case the winner is the player with the control of the most squares of the board. -

    -

    - Both of these need to be checked after each turn and when a winner is found the points are calculated based on the size of the board and the number of pieces left. -

    -

    - Compared to Chess or Go, Tak is a little bit more complicated. - This is not through having a significantly large number of rules but the systems intertwine more and are more flexible. - This does not mean that it is a better game just different, but it has a similar feeling of depth and timelessness. -

    -

    - The board being stored as an array of integers means that it takes very little memory. - That made it simple to store past boards in a stack and implement an undo function. - Both to take back moves and even go back to a previous game from the same session. -

    -

    - I am pleased with how the game turned out. - I am working on other things at the moment but in the future I would like to do two more things with this game. - I want to implement it online so that I can play with friends over the internet. - I would also like to try and develop an A.I. - agent to play the game. - I had this in mind when I was writing it so made sure that the GUI was disconnected from the actual mechanics behind the game. - This will hopefully make it easier to connect to an agent for training. -

    -
    - - -{% endblock %}