mirror of
https://github.com/sammyshear/sshear.dev.git
synced 2025-12-06 03:15:53 +00:00
Blog works
This commit is contained in:
parent
d0ca44c219
commit
ac5d3487e8
@ -3,7 +3,6 @@ import tailwind from "@astrojs/tailwind";
|
|||||||
import react from "@astrojs/react";
|
import react from "@astrojs/react";
|
||||||
import robotsTxt from "astro-robots-txt";
|
import robotsTxt from "astro-robots-txt";
|
||||||
import sitemap from "@astrojs/sitemap";
|
import sitemap from "@astrojs/sitemap";
|
||||||
|
|
||||||
import compressor from "astro-compressor";
|
import compressor from "astro-compressor";
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
@ -15,7 +14,8 @@ export default defineConfig({
|
|||||||
themes: {
|
themes: {
|
||||||
light: "catppuccin-latte",
|
light: "catppuccin-latte",
|
||||||
dark: "catppuccin-mocha"
|
dark: "catppuccin-mocha"
|
||||||
}
|
},
|
||||||
|
wrap: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@catppuccin/tailwindcss": "^0.1.6",
|
"@catppuccin/tailwindcss": "^0.1.6",
|
||||||
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-astro": "^0.13.0"
|
"prettier-plugin-astro": "^0.13.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,9 @@ devDependencies:
|
|||||||
'@catppuccin/tailwindcss':
|
'@catppuccin/tailwindcss':
|
||||||
specifier: ^0.1.6
|
specifier: ^0.1.6
|
||||||
version: 0.1.6(tailwindcss@3.4.1)
|
version: 0.1.6(tailwindcss@3.4.1)
|
||||||
|
'@tailwindcss/typography':
|
||||||
|
specifier: ^0.5.10
|
||||||
|
version: 0.5.10(tailwindcss@3.4.1)
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.2.5
|
specifier: ^3.2.5
|
||||||
version: 3.2.5
|
version: 3.2.5
|
||||||
@ -879,6 +882,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==}
|
resolution: {integrity: sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tailwindcss/typography@0.5.10(tailwindcss@3.4.1):
|
||||||
|
resolution: {integrity: sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==}
|
||||||
|
peerDependencies:
|
||||||
|
tailwindcss: '>=3.0.0 || insiders'
|
||||||
|
dependencies:
|
||||||
|
lodash.castarray: 4.4.0
|
||||||
|
lodash.isplainobject: 4.0.6
|
||||||
|
lodash.merge: 4.6.2
|
||||||
|
postcss-selector-parser: 6.0.10
|
||||||
|
tailwindcss: 3.4.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/babel__core@7.20.5:
|
/@types/babel__core@7.20.5:
|
||||||
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2287,6 +2302,18 @@ packages:
|
|||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/lodash.castarray@4.4.0:
|
||||||
|
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/lodash.isplainobject@4.0.6:
|
||||||
|
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/lodash.merge@4.6.2:
|
||||||
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/log-symbols@5.1.0:
|
/log-symbols@5.1.0:
|
||||||
resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==}
|
resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -3069,6 +3096,14 @@ packages:
|
|||||||
postcss: 8.4.35
|
postcss: 8.4.35
|
||||||
postcss-selector-parser: 6.0.16
|
postcss-selector-parser: 6.0.16
|
||||||
|
|
||||||
|
/postcss-selector-parser@6.0.10:
|
||||||
|
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dependencies:
|
||||||
|
cssesc: 3.0.0
|
||||||
|
util-deprecate: 1.0.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/postcss-selector-parser@6.0.16:
|
/postcss-selector-parser@6.0.16:
|
||||||
resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==}
|
resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|||||||
24
src/components/Post.astro
Normal file
24
src/components/Post.astro
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
import { type CollectionEntry } from "astro:content";
|
||||||
|
import Prose from "./Prose.astro";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
post: CollectionEntry<"blog">;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { post } = Astro.props;
|
||||||
|
const { Content } = await post.render();
|
||||||
|
---
|
||||||
|
|
||||||
|
<article class="ml-5 mr-5 mt-5 w-[90%] sm:w-3/4 md:w-1/2 lg:w-1/3">
|
||||||
|
<header class="flex flex-col space-between-10 border-b-ctp-text border-b">
|
||||||
|
<h1 class="text-2xl">{post.data.title}</h1>
|
||||||
|
<h4 class="text-lg text-ctp-subtext0">{post.data.author}</h4>
|
||||||
|
<h4 class="text-lg text-ctp-subtext0">{post.data.publishedTime}</h4>
|
||||||
|
</header>
|
||||||
|
<main class="mt-5">
|
||||||
|
<Prose>
|
||||||
|
<Content />
|
||||||
|
</Prose>
|
||||||
|
</main>
|
||||||
|
</article>
|
||||||
29
src/components/Posts.tsx
Normal file
29
src/components/Posts.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import type { CollectionEntry } from "astro:content";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
posts: CollectionEntry<"blog">[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Posts({ posts }: Props) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col space-y-10 mt-5">
|
||||||
|
{posts.map((post: CollectionEntry<"blog">) => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
className="flex flex-col bg-ctp-surface1 p-4 rounded-md"
|
||||||
|
key={post.id}
|
||||||
|
href={`/posts/${post.slug}`}
|
||||||
|
>
|
||||||
|
<span className="text-2xl">{post.data.title}</span>
|
||||||
|
<span className="text-sm text-ctp-subtext0">
|
||||||
|
{post.data.author}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-ctp-subtext0">
|
||||||
|
{post.data.publishedTime.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
12
src/components/Prose.astro
Normal file
12
src/components/Prose.astro
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="prose dark:prose-invert
|
||||||
|
prose-h1:font-bold prose-h1:text-xl
|
||||||
|
prose-a:text-blue-600 prose-p:text-justify prose-img:rounded-xl
|
||||||
|
prose-headings:underline"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
@ -1,8 +1,7 @@
|
|||||||
import type { Site } from "./types";
|
import type { Site } from "./types";
|
||||||
|
|
||||||
export const SITE: Site = {
|
export const SITE: Site = {
|
||||||
title: "sshear.dev",
|
title: "sshear.dev",
|
||||||
author: "Sammy Shear",
|
author: "Sammy Shear",
|
||||||
lightAndDarkMode: true,
|
desc: "Sammy's personal site, portfolio, and blog."
|
||||||
postPerPage: 5
|
|
||||||
};
|
};
|
||||||
|
|||||||
207
src/content/blog/i-followed-a-40-year-old-pascal-book-in-rust.md
Normal file
207
src/content/blog/i-followed-a-40-year-old-pascal-book-in-rust.md
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
---
|
||||||
|
title: I Followed a 40 year old Pascal Book (in Rust)
|
||||||
|
publishedTime: 2024-01-05T19:09:05.026Z
|
||||||
|
---
|
||||||
|
|
||||||
|
My father is a big fan of mystery novels, certainly more than I am. I enjoy a Sherlock Holmes story now and again, and I have had fun reading Agatha Christie novels, but he is and always will be a bigger fan of the genre than I. That being said, there is one mystery book I find much more interesting to me than he ever has, and that is "Elementary Pascal" by Henry Ledgard and Andrew Singer. My father will happily tell you the furthest he got in computer programming was making a 3D Cube in Basic and reading the programs for his Commodore 64 in a magazine, but almost never running them as he would have had to copy them each by hand. I, on the other hand, have much more of an interest in programming, as might be evident from the fact I am writing this blog post where I do so. When I was first learning to program as a kid, my father gave me this book that he'd had since he was a kid. He claimed to have hardly read it, but that I might be interested in it, and compared to him I certainly was. That said, I only ever read a few of the examples, as I had very little actual interest in learning Pascal. In more recent years, the book has sat on my shelf, undisturbed, waiting to once again be opened. Today was that day.
|
||||||
|
|
||||||
|
### What is this book?
|
||||||
|
|
||||||
|
This book is a way of teaching Pascal by examples written in the form of Sherlock Holmes mysteries. Each chapter is a story which teaches you core concepts of the both the Pascal language and programming in general.
|
||||||
|
|
||||||
|
### What will I be doing?
|
||||||
|
|
||||||
|
I will be, rewriting the Pascal code presented in the book as Rust mostly for fun, but also to see how programming has evolved between the points of these two languages. If there are any optimizations I notice can be made, I'll do my best to implement them, but that isn't really what I'll be focusing on with this.
|
||||||
|
|
||||||
|
## Part 1 (Chapter 2): Murder at the Metropolitan Club
|
||||||
|
|
||||||
|
If you have this book and are following along at home, you may notice this is technically Chapter 2, but given Chapter 1 is more of an introduction to the concept of a computer than anything else, I figured I'd skip to this chapter for this article.
|
||||||
|
|
||||||
|
This chapter is all about solving a murder based on clues provided to an algorithm. The basic algorithm used in the book is summed up by the pseudocode they provide:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Look at the next clue
|
||||||
|
2. If it establishes a fact then:
|
||||||
|
record that fact
|
||||||
|
else
|
||||||
|
dismiss the clue
|
||||||
|
3. Repeat this process until the murderer is found.
|
||||||
|
```
|
||||||
|
|
||||||
|
The clues provided are as follows:
|
||||||
|
|
||||||
|
1. Sir Raymond Jasper occupied Room 10.
|
||||||
|
2. The man occupying Room 14 had black hair.
|
||||||
|
3. Either Sir Raymond or Colonel Woodley wore a pince-nez.
|
||||||
|
4. Mr. Pope always carried a gold pocket-watch.
|
||||||
|
5. One of the suspects was seen driving a 4-wheel carriage.
|
||||||
|
6. The man with the pince-nez had brown hair.
|
||||||
|
7. Mr. Holman wore a ruby signet ring.
|
||||||
|
8. The man in Room 16 had tattered cuffs.
|
||||||
|
9. Mr. Holman occupied Room 12.
|
||||||
|
10. The man with the tattered cuffs had red hair.
|
||||||
|
11. The man in Room 12 had gray hair.
|
||||||
|
12. The man with a gold pocket watch occupied Room 14.
|
||||||
|
13. Colonel Woodley occupied a corner room.
|
||||||
|
14. The murderer had brown hair.
|
||||||
|
|
||||||
|
This is all the information we have to feed into the algorithm, but there is also a map of the rooms we can use to define what is a corner room. The map shows us that rooms 16 and 10 are corner rooms. For this chapter I will not write any code, as the chapter simply goes over the algorithm and how to create an algorithm in general. In the next chapter we get our first Pascal code, and that's what I will be rewriting.
|
||||||
|
|
||||||
|
## Part 2 (Chapter 3): Holmes Gives a Demonstration
|
||||||
|
|
||||||
|
The Pascal code is fairly interesting to me in the way it approaches the problem. Since I don't really know Pascal all too well, I can't tell you if this was simplified for the purposes of the book or if this is truly the best way to solve this problem. Regardless, I find the code we are given to be fascinating. So as to save me the trouble of transcribing all of it into this article, I will show snippets of the Pascal code with some explanations along the way, then I will show my Rust equivalents.
|
||||||
|
|
||||||
|
```pascal
|
||||||
|
const
|
||||||
|
UNKNOWN = 0;
|
||||||
|
RED = 1; BLACK = 2; GREY = 3; BROWN = 4;
|
||||||
|
PINCENEZ = 1; GOLDWATCH = 2; RUBYRING = 3; TATTEREDCUFFS = 4;
|
||||||
|
COLWOODLEY = 1; MRHOLMAN = 2; MRPOPE = 3; SIRRAYMOND = 4;
|
||||||
|
var
|
||||||
|
SUSPECT, MURDERER: INTEGER;
|
||||||
|
|
||||||
|
HAIR : array [COLWOODLEY .. SIRRAYMOND] of INTEGER;
|
||||||
|
ATTIRE : array [COLWOODLEY .. SIRRAYMOND] of INTEGER;
|
||||||
|
ROOM : array [COLWOODLEY .. SIRRAYMOND] of INTEGER;
|
||||||
|
```
|
||||||
|
|
||||||
|
The first bit of the program assigns the variables and constants that will be used globally in the program. You can probably start piecing together the implementation of the algorithm based on these declarations. There are 3 arrays that are the length of a range between `COLWOODLEY` and `SIRRAYMOND`, each of integers, while each piece of information is defined as a constant between 1 and 4, along with an `UNKNOWN` of 0. The thought process is, of course, that each person represents an index of the arrays, and as such the data of the array `ATTIRE` at that index represents whether they're wearing, say, a ruby ring or a gold watch. The same concept going for the other arrays, of course.
|
||||||
|
|
||||||
|
I probably would never have thought of this approach and would have instead defined structs and enums to do essentially the same thing, but I find this approach very sensical and almost delightfully simple.
|
||||||
|
|
||||||
|
The next bit of the program assigns all the known and unknown pieces of data in the array.
|
||||||
|
|
||||||
|
```pascal
|
||||||
|
MURDERER := UNKNOWN;
|
||||||
|
for SUSPECT := COLWOODLEY to SIRRAYMOND do begin
|
||||||
|
HAIR[SUSPECT] := UNKNOWN;
|
||||||
|
ATTIRE[SUSPECT] := UNKNOWN;
|
||||||
|
ROOM[SUSPECT] := UNKNOWN
|
||||||
|
end;
|
||||||
|
|
||||||
|
ROOM[SIRRAYMOND] := 10;
|
||||||
|
ATTIRE[MRPOPE] := GOLDWATCH;
|
||||||
|
ATTIRE[MRHOLMAN] := RUBYRING;
|
||||||
|
ROOM[MRHOLMAN] := 12;
|
||||||
|
```
|
||||||
|
|
||||||
|
I don't think there's much explaining that needs to be done for that snippet. Anyway, for this next bit I won't be typing out each if statement, I'll instead show the while loop definition and a few of the if statements for assigning data, mostly the ones in which I find the syntax particularly interesting.
|
||||||
|
|
||||||
|
```pascal
|
||||||
|
SUSPECT := COLWOODLEY;
|
||||||
|
|
||||||
|
while MURDERER = UNKNOWN do begin
|
||||||
|
if (ROOM[SUSPECT] = 14) then
|
||||||
|
HAIR[SUSPECT] := BLACK;
|
||||||
|
if (ATTIRE[SIRRAYMOND]<>UNKNOWN) and (ATTIRE[SIRRAYMOND]<>PINCENEZ) then
|
||||||
|
ATTIRE[COLWOODLEY] := PINCENEZ
|
||||||
|
...
|
||||||
|
if (ATTIRE[SUSPECT] = PINCENEZ) then
|
||||||
|
HAIR[SUSPECT] := BROWN;
|
||||||
|
...
|
||||||
|
if (HAIR[SUSPECT] := BROWN) then
|
||||||
|
MURDERER := SUSPECT;
|
||||||
|
if (SUSPECT = SIRRAYMOND) then
|
||||||
|
SUSPECT := COLWOODLEY; { -- prevents an index out of bounds }
|
||||||
|
else
|
||||||
|
SUSPECT := SUSPECT + 1;
|
||||||
|
end;
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see from the basic structure, as long as the murderer is still unknown, it will perform an iteration where it tries to assign new information to people based on the clues, then increments (or sets back to `COLWOODLEY`) the suspect for the next iteration. Also of note, to me at least, is the not equal to operator being `<>`. At this point you've probably also noticed that the assignment operator is `:=`, much like Go, which frees up `=` to be the equal to operator. The final thing to note is that earlier when the arrays were being declared, you may have noticed the range operator is very similar to Rust, which I didn't know before starting this post, and found quite interesting.
|
||||||
|
|
||||||
|
Anyway, after this while loop is finished, it checks who the murderer is and prints it to the standard output with `WRITE`.
|
||||||
|
|
||||||
|
Overall it's a very simple program, which is refreshing for me because I have a bad habit of overengineering things, so seeing it done this way is very helpful to not do the same in my Rust solution.
|
||||||
|
|
||||||
|
### Rust
|
||||||
|
|
||||||
|
Now it's time to implement it in Rust. I will be doing a 1 for 1 transcription of the Pascal code to Rust, but in future chapters that may change.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const UNKNOWN: u8 = 0;
|
||||||
|
const RED: u8 = 1;
|
||||||
|
const PINCENEZ: u8 = 1;
|
||||||
|
const COLWOODLEY: usize = 1;
|
||||||
|
const BLACK: u8 = 2;
|
||||||
|
const GOLDWATCH: u8 = 2;
|
||||||
|
const MRHOLMAN: usize = 2;
|
||||||
|
const GREY: u8 = 3;
|
||||||
|
const RUBYRING: u8 = 3;
|
||||||
|
const MRPOPE: usize = 3;
|
||||||
|
const BROWN: u8 = 4;
|
||||||
|
const TATTEREDCUFFS: u8 = 4;
|
||||||
|
const SIRRAYMOND: usize = 4;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut murderer = UNKNOWN;
|
||||||
|
let mut hair: Vec<u8> = vec![UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN]; // dummy value at 0 to keep parity with Pascal
|
||||||
|
let mut attire: Vec<u8> = vec![UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN];
|
||||||
|
let mut room: Vec<u8> = vec![UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN];
|
||||||
|
|
||||||
|
// establish known clues
|
||||||
|
|
||||||
|
room[SIRRAYMOND as usize] = 10;
|
||||||
|
attire[MRPOPE as usize] = GOLDWATCH;
|
||||||
|
attire[MRHOLMAN as usize] = RUBYRING;
|
||||||
|
room[MRHOLMAN as usize] = 12;
|
||||||
|
|
||||||
|
let mut suspect = COLWOODLEY;
|
||||||
|
|
||||||
|
while murderer == UNKNOWN {
|
||||||
|
if room[suspect] == 14 {
|
||||||
|
hair[suspect] = BLACK;
|
||||||
|
}
|
||||||
|
if attire[SIRRAYMOND] != UNKNOWN && attire[SIRRAYMOND] != PINCENEZ {
|
||||||
|
attire[COLWOODLEY] = PINCENEZ;
|
||||||
|
}
|
||||||
|
if attire[COLWOODLEY] != UNKNOWN && attire[COLWOODLEY] != PINCENEZ {
|
||||||
|
attire[SIRRAYMOND] = PINCENEZ;
|
||||||
|
}
|
||||||
|
if attire[suspect] == PINCENEZ {
|
||||||
|
hair[suspect] = BROWN;
|
||||||
|
}
|
||||||
|
if attire[suspect] == TATTEREDCUFFS {
|
||||||
|
hair[suspect] = RED;
|
||||||
|
}
|
||||||
|
if room[suspect] == 16 {
|
||||||
|
attire[suspect] = TATTEREDCUFFS;
|
||||||
|
}
|
||||||
|
if room[suspect] == 12 {
|
||||||
|
hair[suspect] = GREY;
|
||||||
|
}
|
||||||
|
if attire[suspect] == GOLDWATCH {
|
||||||
|
room[suspect] = 14;
|
||||||
|
}
|
||||||
|
if room[suspect] == 10 && suspect != COLWOODLEY {
|
||||||
|
room[COLWOODLEY] = 16;
|
||||||
|
}
|
||||||
|
if room[suspect] == 16 && suspect != COLWOODLEY {
|
||||||
|
room[COLWOODLEY] = 10;
|
||||||
|
}
|
||||||
|
if hair[suspect] == BROWN {
|
||||||
|
murderer = suspect as u8;
|
||||||
|
}
|
||||||
|
if suspect == SIRRAYMOND {
|
||||||
|
suspect = COLWOODLEY;
|
||||||
|
} else {
|
||||||
|
suspect = suspect + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if suspect == COLWOODLEY {
|
||||||
|
println!("Colonel Woodley is the murderer.");
|
||||||
|
}
|
||||||
|
if suspect == MRHOLMAN {
|
||||||
|
println!("Mr. Holman is the murderer.");
|
||||||
|
}
|
||||||
|
if suspect == MRPOPE {
|
||||||
|
println!("Mr. Pope is the murderer.");
|
||||||
|
}
|
||||||
|
if suspect == SIRRAYMOND {
|
||||||
|
println!("Sir Raymond is the murderer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That's about it for this chapter, and so I will end the post here. I will in all likelihood be making this a series, so if you're interested, stay tuned for post number 2 where I'll try to tackle the fourth chapter, "The Adventure of the Bathing Machine." (These first two chapters are more of a warm up for the format of the book, the following chapters will each be their own mystery).
|
||||||
13
src/content/config.ts
Normal file
13
src/content/config.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { SITE } from "../config";
|
||||||
|
import { defineCollection, z } from "astro:content";
|
||||||
|
|
||||||
|
const blog = defineCollection({
|
||||||
|
type: "content",
|
||||||
|
schema: z.object({
|
||||||
|
author: z.string().default(SITE.author),
|
||||||
|
title: z.string(),
|
||||||
|
publishedTime: z.date()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const collections = { blog };
|
||||||
@ -1,5 +1,16 @@
|
|||||||
---
|
---
|
||||||
import { SITE } from "../config";
|
import { SITE } from "../config";
|
||||||
|
export interface Props {
|
||||||
|
title?: string;
|
||||||
|
author?: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
title = SITE.title,
|
||||||
|
author = SITE.author,
|
||||||
|
description = SITE.desc
|
||||||
|
} = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@ -8,7 +19,10 @@ import { SITE } from "../config";
|
|||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<title>{SITE.title}</title>
|
<meta name="author" content={author} />
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||||
|
<title>{title}</title>
|
||||||
</head>
|
</head>
|
||||||
<body
|
<body
|
||||||
class="flex flex-col place-items-center bg-ctp-base ctp-latte text-ctp-text dark:ctp-mocha"
|
class="flex flex-col place-items-center bg-ctp-base ctp-latte text-ctp-text dark:ctp-mocha"
|
||||||
@ -17,6 +31,18 @@ import { SITE } from "../config";
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
<style is:global>
|
||||||
|
html.dark .astro-code,
|
||||||
|
html.dark .astro-code span {
|
||||||
|
color: var(--shiki-dark) !important;
|
||||||
|
background-color: var(--shiki-dark-bg) !important;
|
||||||
|
/* Optional, if you also want font styles */
|
||||||
|
font-style: var(--shiki-dark-font-style) !important;
|
||||||
|
font-weight: var(--shiki-dark-font-weight) !important;
|
||||||
|
text-decoration: var(--shiki-dark-text-decoration) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
const theme = (() => {
|
const theme = (() => {
|
||||||
if (
|
if (
|
||||||
|
|||||||
18
src/layouts/PostLayout.astro
Normal file
18
src/layouts/PostLayout.astro
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
import Layout from "./Layout.astro";
|
||||||
|
import Header from "../components/Header.astro";
|
||||||
|
import { SITE } from "../config";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
frontmatter: {
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { frontmatter } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout title={`${frontmatter.title} | ${SITE.title}`}>
|
||||||
|
<Header />
|
||||||
|
<slot />
|
||||||
|
</Layout>
|
||||||
18
src/layouts/PostsLayout.astro
Normal file
18
src/layouts/PostsLayout.astro
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
import Layout from "./Layout.astro";
|
||||||
|
import Header from "../components/Header.astro";
|
||||||
|
import { SITE } from "../config";
|
||||||
|
import { type CollectionEntry } from "astro:content";
|
||||||
|
import Posts from "../components/Posts";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
posts: CollectionEntry<"blog">[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { posts } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout title={`Blog Posts | ${SITE.title}`}>
|
||||||
|
<Header />
|
||||||
|
<Posts posts={posts} />
|
||||||
|
</Layout>
|
||||||
@ -5,4 +5,27 @@ import Layout from "../layouts/Layout.astro";
|
|||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Header />
|
<Header />
|
||||||
|
<main
|
||||||
|
class="ml-5 mr-5 mt-5 w-[90%] sm:w-3/4 md:w-1/2 lg:w-1/3 flex flex-col space-between-5"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Hi, I'm Sammy Shear, welcome to my personal site and blog. I'm a
|
||||||
|
developer with experience in both Web and Native development, but I
|
||||||
|
have been mostly focused on the Web. I have experience in backend
|
||||||
|
and frontend. On the frontend I mostly do work in React and Vue (and
|
||||||
|
I am partial to HTMX), but over the years I've used many other
|
||||||
|
frameworks and technologies in general. On the backend at this point
|
||||||
|
I most often use Rust with Actix Web or Axum, but I also have
|
||||||
|
experience with Node and Express, as well as RPC using tRPC (a
|
||||||
|
TypeScript RPC implementation).
|
||||||
|
<br />
|
||||||
|
This site mostly serves as a blog for me to write about things I do or
|
||||||
|
am interested in, but I will probably add more uses for the site as I
|
||||||
|
think of them.
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
class="text-ctp-base bg-ctp-sky hover:bg-ctp-blue focus:ring-4 focus:ring-ctp-blue font-medium rounded-lg text-sm px-5 py-2.5 focus:outline-none place-self-center"
|
||||||
|
href="posts/">Blog</a
|
||||||
|
>
|
||||||
|
</main>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
26
src/pages/posts/[slug]/index.astro
Normal file
26
src/pages/posts/[slug]/index.astro
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
import PostLayout from "../../../layouts/PostLayout.astro";
|
||||||
|
import Post from "../../../components/Post.astro";
|
||||||
|
import { getCollection, type CollectionEntry } from "astro:content";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
post: CollectionEntry<"blog">;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const posts = await getCollection("blog");
|
||||||
|
|
||||||
|
const postResult = posts.map((post) => ({
|
||||||
|
params: { slug: post.slug },
|
||||||
|
props: { post }
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [...postResult];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { post } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<PostLayout frontmatter={{ title: post.data.title }}>
|
||||||
|
<Post post={post} />
|
||||||
|
</PostLayout>
|
||||||
8
src/pages/posts/index.astro
Normal file
8
src/pages/posts/index.astro
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
import { getCollection } from "astro:content";
|
||||||
|
import PostsLayout from "../../layouts/PostsLayout.astro";
|
||||||
|
|
||||||
|
const posts = await getCollection("blog");
|
||||||
|
---
|
||||||
|
|
||||||
|
<PostsLayout posts={posts} />
|
||||||
@ -1,6 +1,5 @@
|
|||||||
export type Site = {
|
export type Site = {
|
||||||
|
desc: string;
|
||||||
title: string;
|
title: string;
|
||||||
author: string;
|
author: string;
|
||||||
lightAndDarkMode: boolean;
|
|
||||||
postPerPage: number;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -32,6 +32,7 @@ export default {
|
|||||||
plugins: [
|
plugins: [
|
||||||
catppuccin({
|
catppuccin({
|
||||||
prefix: "ctp"
|
prefix: "ctp"
|
||||||
})
|
}),
|
||||||
|
require("@tailwindcss/typography")
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user