Compare commits

..

28 Commits

Author SHA1 Message Date
8232bf66ee Slightly less cantankerous mini TCP echo server 2025-07-07 21:46:40 -04:00
8b39d6cc82 Teensy TCP echo server 2025-07-07 21:23:01 -04:00
0b2d033cd8 Why am I even doing this 2025-07-05 09:12:17 -04:00
f134ca4979 An mess 2025-07-05 07:40:54 -04:00
7fb96a8db7 Remove piston tutorials 2025-02-23 11:44:13 -05:00
cf91122a57 Building some sweep tag I think! 2024-11-30 22:59:33 -05:00
174c101fb1 Such temperature 2024-11-30 19:53:05 -05:00
4e7e47dd94 Doing a cc4e 2024-11-30 15:53:33 -05:00
a5ce30aeeb Up through tutorial part 3, the first bit 2024-09-16 06:56:04 -04:00
11d4baf648 Adding polls app while working through django tutorial 2024-09-15 21:26:11 -04:00
b1197d5c6a Ruffed up 2024-09-14 21:36:07 -04:00
2be95276a5 Oh djoy 2024-09-14 21:34:50 -04:00
3c8412d858 Playing with binary IO 2024-05-03 23:27:56 -04:00
ac113056a8 Playing with binary read/write 2024-05-03 22:37:15 -04:00
7241713b3f Making a mess of ex17 extra credit (NOT WORKING) 2024-05-03 21:50:46 -04:00
0d19b4cb4f Doing nully things with strncpy 2024-04-29 08:12:17 -04:00
1a0ba35f7a Up through happy path of lcthw ex17 2024-04-28 22:56:59 -04:00
869f273723 Do the lcthw ex16 extra credit with stack structs 2024-04-28 22:10:16 -04:00
4c2c6ef429 Goofing around with that intro to algorithms book again 2024-04-28 22:02:16 -04:00
e5166ee912 Sorting negative cards 2024-04-27 22:22:56 -04:00
6ea704fe5b Add an assertion to Person_print 2024-04-20 13:01:33 -04:00
01348b1436 Up through lcthw exercise 16 2024-04-20 12:41:53 -04:00
2dfa546e78 Something like ex15 extra credit 2024-04-20 08:38:27 -04:00
224300e1cf Up through lcthw ex 15 2024-04-19 21:51:42 -04:00
0ce1da0737 Fold up a few lines 2024-04-19 21:26:19 -04:00
b979a794d6 lcthw ex14 extra credit 2024-04-19 21:22:10 -04:00
d2d54c12a8 Read argv files via readarray 2024-04-19 21:11:24 -04:00
8ac30a239d Up through lcthw ex14 2024-04-19 20:53:48 -04:00
58 changed files with 1910 additions and 4452 deletions

19
cc4e/fc.c Normal file
View File

@@ -0,0 +1,19 @@
#include <stdio.h>
/* print Fahrenheit-Celsius table
for f = 0, 20, ..., 300 */
int main() {
int lower, upper, step;
float fahr, celsius;
lower = 0; /* lower limit of temperature table */
upper = 300; /* upper limit */
step = 20; /* step size */
fahr = lower;
while (fahr <= upper) {
celsius = (5.0/9.0) * (fahr-32.0);
printf("%4.0f %6.1f\n", fahr, celsius);
fahr = fahr + step;
}
}

12
cc4e/fc_2.c Normal file
View File

@@ -0,0 +1,12 @@
#include <stdio.h>
#define LOWER 0 /* lower limit of table */
#define UPPER 300 /* upper limit */
#define STEP 20 /* step size */
int main() { /* Fahrenheit-Celsius table */
int fahr;
for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP)
printf("%4d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));
}

5
cc4e/hello.c Normal file
View File

@@ -0,0 +1,5 @@
#include <stdio.h>
int main() {
printf("hello, world\n");
return 0;
}

2
djoy/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.venv/
*.sqlite3

1
djoy/.python-version Normal file
View File

@@ -0,0 +1 @@
3.12.6

0
djoy/djoy/__init__.py Normal file
View File

16
djoy/djoy/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for djoy project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djoy.settings")
application = get_asgi_application()

125
djoy/djoy/settings.py Normal file
View File

@@ -0,0 +1,125 @@
"""
Django settings for djoy project.
Generated by 'django-admin startproject' using Django 5.1.1.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
import os
import pathlib
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = pathlib.Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("DJOY_SECRET_KEY", "django-insecure-0xohno")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"polls.apps.PollsConfig",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "djoy.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "djoy.wsgi.application"
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = "static/"
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

24
djoy/djoy/urls.py Normal file
View File

@@ -0,0 +1,24 @@
"""
URL configuration for djoy project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("polls/", include("polls.urls")),
path("admin/", admin.site.urls),
]

16
djoy/djoy/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for djoy project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djoy.settings")
application = get_wsgi_application()

23
djoy/manage.py Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djoy.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()

0
djoy/polls/__init__.py Normal file
View File

5
djoy/polls/admin.py Normal file
View File

@@ -0,0 +1,5 @@
from django.contrib import admin
from .models import Question
admin.site.register(Question)

6
djoy/polls/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class PollsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "polls"

View File

@@ -0,0 +1,51 @@
# Generated by Django 5.1.1 on 2024-09-16 01:23
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Question",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("question_text", models.CharField(max_length=200)),
("pub_date", models.DateTimeField(verbose_name="date published")),
],
),
migrations.CreateModel(
name="Choice",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("choice_text", models.CharField(max_length=200)),
("votes", models.IntegerField(default=0)),
(
"question",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="polls.question"
),
),
],
),
]

View File

24
djoy/polls/models.py Normal file
View File

@@ -0,0 +1,24 @@
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField("date published")
def __str__(self):
return self.question_text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text

3
djoy/polls/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

11
djoy/polls/urls.py Normal file
View File

@@ -0,0 +1,11 @@
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("<int:question_id>/", views.detail, name="detail"),
path("<int:question_id>/results/", views.results, name="results"),
path("<int:question_id>/vote/", views.vote, name="vote"),
]

26
djoy/polls/views.py Normal file
View File

@@ -0,0 +1,26 @@
from django.http import HttpResponse
from .models import Question
def index(request) -> HttpResponse:
latest_question_list = Question.objects.order_by("-pub_date")[:5]
output = ", ".join([q.question_text for q in latest_question_list])
return HttpResponse(output.encode())
def detail(request, question_id) -> HttpResponse:
_ = request
return HttpResponse(f"You're looking at question {question_id}.".encode())
def results(request, question_id) -> HttpResponse:
_ = request
return HttpResponse(
f"You're looking at the results of question {question_id}".encode()
)
def vote(request, question_id) -> HttpResponse:
_ = request
return HttpResponse(f"You're voting on question {question_id}".encode())

3
djoy/requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
Django
django-stubs
ruff

View File

@@ -0,0 +1,62 @@
package main
import (
"fmt"
"os"
"strconv"
"strings"
)
func main() {
inBytes, err := os.ReadFile(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
os.Exit(1)
}
inLines := strings.Split(strings.TrimSpace(string(inBytes)), "\n")
inInts := make([]int64, len(inLines))
for i, line := range inLines {
v, err := strconv.ParseInt(line, 0, 64)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
os.Exit(1)
}
inInts[i] = v
}
if err := insertionSort(inInts); err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
os.Exit(1)
}
for _, i := range inInts {
fmt.Printf("%v\n", i)
}
}
func insertionSort(inInts []int64) error {
if len(inInts) < 2 {
return nil
}
for j := 1; j < len(inInts); j += 1 {
key := inInts[j]
fmt.Fprintf(os.Stderr, "%+v (save %v from %v)\n", inInts, key, j)
i := j - 1
for ; i > -1 && inInts[i] > key; i -= 1 {
inInts[i+1] = inInts[i]
fmt.Fprintf(os.Stderr, "%+v (j=%v i=%v)\n", inInts, j, i)
}
inInts[i+1] = key
fmt.Fprintf(os.Stderr, "%+v (insert %v at %v)\n", inInts, key, i+1)
}
return nil
}

View File

@@ -0,0 +1,38 @@
import os
import sys
def main() -> int:
in_ints: list[int] = []
with open(sys.argv[1]) as infile:
for line in infile:
in_ints.append(int(line.strip()))
insertion_sort(in_ints)
for i in in_ints:
print(i)
return 0
def insertion_sort(in_ints: list[int]) -> None:
for i in range(len(in_ints) - 1):
j = i + 1
key = in_ints[j]
print(f"{in_ints} (save {key} from {j})", file=sys.stderr)
while i > -1 and in_ints[i] > key:
in_ints[i + 1] = in_ints[i]
print(f"{in_ints} (j={j} i={i})", file=sys.stderr)
i -= 1
in_ints[i + 1] = key
print(f"{in_ints} (insert {key} at {i+1})", file=sys.stderr)
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,10 @@
11
2
17
-1
34
8
999
2
-56
3

5
lcthw/.ex14.argv Normal file
View File

@@ -0,0 +1,5 @@
eggs
legs
pegs
jeggings
banana pants

2
lcthw/.ex17.argv Normal file
View File

@@ -0,0 +1,2 @@
ex17.db
l

2
lcthw/.ex17ec.argv Normal file
View File

@@ -0,0 +1,2 @@
ex17ec.db
l

1
lcthw/.ex17play.argv Normal file
View File

@@ -0,0 +1 @@
ex17play.bin

11
lcthw/.gitignore vendored
View File

@@ -1,9 +1,2 @@
ex1
ex3
ex7
ex8
ex9
ex10
ex11
ex12
ex13
ex*
!ex*.c

View File

@@ -1,3 +1,5 @@
SHELL := /bin/bash
CFLAGS ?= -Wall -g
GDBRUN = gdb --batch --ex run --ex bt --ex q --args
BUILD_TARGETS = $(foreach ex,$(wildcard ex*.c),$(subst .c,,$(ex)))
@@ -17,19 +19,17 @@ clean:
build: $(BUILD_TARGETS)
.PHONY: gtest
gtest:
@$(foreach bt, $(BUILD_TARGETS), make .gtest.$(bt) &&) \
printf '\ngYAY\n'
gtest: $(BUILD_TARGETS)
@$(foreach bt, $(BUILD_TARGETS), make .gtest.$(bt) &&) printf '\ngYAY\n'
.gtest.%:
@test_argv=$$(cat .$*.argv 2>/dev/null || echo) && \
printf '\n==> %s\n' "$*" && $(GDBRUN) ./$*
.gtest.%: %
@if test -f .$*.argv; then readarray -t test_argv <.$*.argv; fi && \
printf '\n==> %s\n' "$*" && $(GDBRUN) ./$* "$${test_argv[@]}"
.PHONY: test
test:
@$(foreach bt, $(BUILD_TARGETS), make .test.$(bt) &&) \
printf '\nYAY\n'
test: $(BUILD_TARGETS)
@$(foreach bt, $(BUILD_TARGETS), make .test.$(bt) &&) printf '\nYAY\n'
.test.%:
@test_argv=$$(cat .$*.argv 2>/dev/null || echo) && \
printf '\n==> %s\n' "$*" && ./$* $$test_argv
.test.%: %
@if test -f .$*.argv; then readarray -t test_argv <.$*.argv; fi && \
printf '\n==> %s\n' "$*" && ./$* "$${test_argv[@]}"

36
lcthw/ex14.c Normal file
View File

@@ -0,0 +1,36 @@
#include <stdio.h>
#include <ctype.h>
#include <string.h>
// forward declarations
void print_letters(size_t len, char arg[]);
void print_arguments(int argc, char *argv[])
{
int i = 0;
for (i = 0; i < argc; i++) {
print_letters(strlen(argv[i]), argv[i]);
}
}
void print_letters(size_t len, char arg[])
{
int i = 0;
for (i = 0; i < len; i++) {
char ch = arg[i];
if (isprint((int)ch)) {
printf("'%c' == %d ", ch, ch);
}
}
printf("\n");
}
int main(int argc, char *argv[])
{
print_arguments(argc, argv);
return 0;
}

56
lcthw/ex15.c Normal file
View File

@@ -0,0 +1,56 @@
#include <stdio.h>
int main(int argc, char *argv[])
{
// create two arrays we care about
int ages[] = { 23, 43, 12, 89, 2 };
char *names[] = {
"Alan", "Frank",
"Mary", "John", "Lisa"
};
// safely get the size of ages
int count = sizeof(ages) / sizeof(int);
int i = 0;
char **first_name = &names[0];
char **last_name = &names[count - 1];
printf("first_name=%p last_name=%p\n", first_name, last_name);
printf("*first_name=%s *last_name=%s\n", *first_name, *last_name);
printf("---\n");
// first way using indexing
for (i = count - 1; i > -1; i--) {
printf("%s has %d years alive.\n", names[i], ages[i]);
}
printf("---\n");
// setup the pointers to the start of the arrays
int *cur_age = ages;
char **cur_name = names;
// second way using pointers
for (i = count - 1; i > -1; i--) {
printf("%s is %d years old.\n", *(cur_name + i), *(cur_age + i));
}
printf("---\n");
// third way, pointers are just arrays
for (i = count - 1; i > -1; i--) {
printf("%s is %d years old again.\n", cur_name[i], cur_age[i]);
}
printf("---\n");
// fourth way with pointers in a stupid complex way
for (cur_name = names, cur_age = ages;
(cur_age - ages) < count; cur_name++, cur_age++) {
printf("%s lived %d years so far.\n", *cur_name, *cur_age);
}
return 0;
}

73
lcthw/ex16.c Normal file
View File

@@ -0,0 +1,73 @@
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char *name;
int age;
int height;
int weight;
};
struct Person *Person_create(char *name, int age, int height, int weight)
{
struct Person *who = malloc(sizeof(struct Person));
assert(who != NULL);
who->name = strdup(name);
who->age = age;
who->height = height;
who->weight = weight;
return who;
}
void Person_destroy(struct Person *who)
{
assert(who != NULL);
free(who->name);
free(who);
}
void Person_print(struct Person *who)
{
assert(who != NULL);
printf("Name: %s\n", who->name);
printf("\tAge: %d\n", who->age);
printf("\tHeight: %d\n", who->height);
printf("\tWeight: %d\n", who->weight);
}
int main(int argc, char *argv[])
{
// make two people structures
struct Person *joe = Person_create("Joe Alex", 32, 64, 100);
struct Person *frank = Person_create("Frank Blank", 20, 72, 180);
// print them out and where they are in memory
printf("Joe is at memory location %p:\n", joe);
Person_print(joe);
printf("Frank is at memory location %p:\n", frank);
Person_print(frank);
// make everyone age 20 years and print them again
joe->age += 20;
joe->height -= 2;
joe->weight += 40;
Person_print(joe);
frank->age += 20;
frank->weight += 20;
Person_print(frank);
// destroy them both so we clean up
Person_destroy(joe);
Person_destroy(frank);
return 0;
}

57
lcthw/ex16ec.c Normal file
View File

@@ -0,0 +1,57 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char *name;
int age;
int height;
int weight;
};
struct Person Person_create(char *name, int age, int height, int weight)
{
struct Person who;
who.name = strdup(name);
who.age = age;
who.height = height;
who.weight = weight;
return who;
}
void Person_print(struct Person who)
{
printf("Name: %s\n", who.name);
printf("\tAge: %d\n", who.age);
printf("\tHeight: %d\n", who.height);
printf("\tWeight: %d\n", who.weight);
}
int main(int argc, char *argv[])
{
// make two people structures
struct Person joe = Person_create("Joe Alex", 32, 64, 100);
struct Person frank = Person_create("Frank Blank", 20, 72, 180);
// print them out and where they are in memory
printf("Joe is at memory location %p:\n", &joe);
Person_print(joe);
printf("Frank is at memory location %p:\n", &frank);
Person_print(frank);
// make everyone age 20 years and print them again
joe.age += 20;
joe.height -= 2;
joe.weight += 40;
Person_print(joe);
frank.age += 20;
frank.weight += 20;
Person_print(frank);
return 0;
}

237
lcthw/ex17.c Normal file
View File

@@ -0,0 +1,237 @@
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define MAX_DATA 512
#define MAX_ROWS 100
struct Address {
int id;
int set;
char name[MAX_DATA];
char email[MAX_DATA];
};
struct Database {
struct Address rows[MAX_ROWS];
};
struct Connection {
FILE *file;
struct Database *db;
};
void die(const char *message)
{
if (errno) {
perror(message);
} else {
printf("ERROR: %s\n", message);
}
exit(1);
}
void Address_print(struct Address *addr)
{
printf("%d %s %s\n", addr->id, addr->name, addr->email);
}
void Database_load(struct Connection *conn)
{
int rc = fread(conn->db, sizeof(struct Database), 1, conn->file);
if (rc != 1) {
die("Failed to load database.");
}
}
struct Connection *Database_open(const char *filename, char mode)
{
struct Connection *conn = malloc(sizeof(struct Connection));
if (!conn) {
die("Memory error");
}
conn->db = malloc(sizeof(struct Database));
if (!conn->db) {
die("Memory error");
}
if (mode == 'c') {
conn->file = fopen(filename, "w");
} else {
conn->file = fopen(filename, "r+");
if (conn->file) {
Database_load(conn);
}
}
if (!conn->file) {
die("Failed to open file");
}
return conn;
}
void Database_close(struct Connection *conn)
{
if (conn) {
if (conn->file) {
fclose(conn->file);
}
if (conn->db) {
free(conn->db);
}
free(conn);
}
}
void Database_write(struct Connection *conn)
{
rewind(conn->file);
int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
if (rc != 1) {
die("Failed to write database.");
}
rc = fflush(conn->file);
if (rc == -1) {
die("Cannot flush database");
}
}
void Database_create(struct Connection *conn)
{
int i = 0;
for (i = 0; i < MAX_ROWS; i++) {
struct Address addr = {.id = i, .set = 0};
conn->db->rows[i] = addr;
}
}
void Database_set(struct Connection *conn, int id, const char *name, const char *email)
{
struct Address *addr = &conn->db->rows[id];
if (addr->set) {
die("Already set, delete it first");
}
addr->set = 1;
char *res = strncpy(addr->name, name, MAX_DATA);
if (!res) {
die("Name copy failed");
}
addr->name[MAX_DATA - 1] = '\0';
res = strncpy(addr->email, email, MAX_DATA);
if (!res) {
die("Email copy failed");
}
addr->email[MAX_DATA - 1] = '\0';
}
void Database_get(struct Connection *conn, int id)
{
struct Address *addr = &conn->db->rows[id];
if (addr->set) {
Address_print(addr);
} else {
die("ID is not set");
}
}
void Database_delete(struct Connection *conn, int id)
{
struct Address addr = {.id = id, .set = 0};
conn->db->rows[id] = addr;
}
void Database_list(struct Connection *conn)
{
int i = 0;
struct Database *db = conn->db;
for (i = 0; i < MAX_ROWS; i++) {
struct Address *cur = &db->rows[i];
if (cur->set) {
Address_print(cur);
}
}
}
int main(int argc, char *argv[])
{
if (argc < 3) {
die("USAGE: ex17 <dbfile> <action> [action params]");
}
char *filename = argv[1];
char action = argv[2][0];
struct Connection *conn = Database_open(filename, action);
int id = 0;
if (argc > 3) {
id = atoi(argv[3]);
}
if (id >= MAX_ROWS) {
die("There's not that many records.");
}
switch (action) {
case 'c':
Database_create(conn);
Database_write(conn);
break;
case 'g':
if (argc != 4) {
die("Need an id to get");
}
Database_get(conn, id);
break;
case 's':
if (argc != 6) {
die("Need an id, name, email to set");
}
Database_set(conn, id, argv[4], argv[5]);
Database_write(conn);
break;
case 'd':
if (argc != 4) {
die("Need an id to delete");
}
Database_delete(conn, id);
Database_write(conn);
break;
case 'l':
Database_list(conn);
break;
default:
die("Invalid action: c=create, g=get, s=set, d=del, l=list");
}
Database_close(conn);
return 0;
}

305
lcthw/ex17ec.c Normal file
View File

@@ -0,0 +1,305 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#define DEFAULT_MAX_DATA 512
#define DEFAULT_MAX_ROWS 100
struct Address {
char *name;
char *email;
};
struct TableOfContents {
size_t n_rows;
size_t string_field_size;
};
struct Database {
struct Address *rows;
};
struct Connection {
FILE *file;
struct TableOfContents *toc;
struct Database *db;
};
void Database_close(struct Connection *conn);
struct Address *Database_get(struct Connection *conn, const char *email);
void die(struct Connection *conn, const char *message)
{
Database_close(conn);
if (errno) {
perror(message);
} else {
printf("ERROR: %s\n", message);
}
exit(1);
}
void Address_print(struct Address *addr)
{
if (!addr) {
return;
}
printf("%s %s\n", addr->name, addr->email);
}
size_t Database_addr_size(struct Connection *conn)
{
return sizeof(struct Address) +
/* name */ conn->toc->string_field_size +
/* email */ conn->toc->string_field_size;
}
size_t Database_size(struct Connection *conn)
{
return sizeof(struct Database) +
(conn->toc->n_rows * Database_addr_size(conn));
}
size_t Database_n_rows(struct Connection *conn)
{
return sizeof(conn->db->rows) / Database_addr_size(conn);
}
void Database_load(struct Connection *conn)
{
int rc = 0;
rc = fread(conn->toc, sizeof(struct TableOfContents), 1, conn->file);
if (rc != 1) {
die(conn, "Failed to load TableOfContents.");
}
rc = fread(conn->db, Database_size(conn), 1, conn->file);
if (rc != 1) {
die(conn, "Failed to load Database.");
}
}
struct Connection *Database_open(const char *filename, char mode, size_t string_field_size)
{
struct Connection *conn = malloc(sizeof(struct Connection));
if (!conn) {
die(conn, "Memory error allocating Connection");
}
conn->toc = malloc(sizeof(struct TableOfContents));
if (!conn->toc) {
die(conn, "Memory error allocating TableOfContents");
}
conn->toc->string_field_size = string_field_size;
conn->db = malloc(sizeof(struct Database));
if (!conn->db) {
die(conn, "Memory error allocating Database");
}
if (mode == 'c') {
conn->file = fopen(filename, "w");
conn->toc->n_rows = 1;
conn->db->rows = malloc(Database_addr_size(conn));
if (!conn->db->rows) {
die(conn, "Memory error allocating Database rows");
}
} else {
conn->file = fopen(filename, "r+");
if (conn->file) {
Database_load(conn);
}
}
if (!conn->file) {
die(conn, "Failed to open file");
}
return conn;
}
void Database_close(struct Connection *conn)
{
if (conn) {
if (conn->file) {
fclose(conn->file);
}
if (conn->toc) {
free(conn->toc);
}
if (conn->db) {
free(conn->db);
}
free(conn);
}
}
void Database_write(struct Connection *conn)
{
rewind(conn->file);
int rc = 0;
rc = fwrite(conn->toc, sizeof(struct TableOfContents), 1, conn->file);
if (rc != 1) {
die(conn, "Failed to write TableOfContents.");
}
rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
if (rc != 1) {
die(conn, "Failed to write Database.");
}
rc = fflush(conn->file);
if (rc == -1) {
die(conn, "Cannot flush");
}
}
void Database_set(struct Connection *conn, const char *name, const char *email)
{
struct Address *addr = Database_get(conn, email);
if (!addr) {
addr = malloc(Database_addr_size(conn));
conn->db->rows[Database_n_rows(conn)+1] = *addr;
}
char *res = strncpy(addr->name, name, conn->toc->string_field_size);
if (!res) {
die(conn, "Name copy failed");
}
addr->name[conn->toc->string_field_size - 1] = '\0';
res = strncpy(addr->email, email, conn->toc->string_field_size);
if (!res) {
die(conn, "Email copy failed");
}
addr->email[conn->toc->string_field_size - 1] = '\0';
}
struct Address *Database_get(struct Connection *conn, const char *email)
{
int i = 0;
struct Address *addr;
for (i = 0; i < Database_n_rows(conn); i++) {
addr = &conn->db->rows[i];
if (strcmp(addr->email, email) == 0) {
return addr;
}
}
return NULL;
}
void Database_delete(struct Connection *conn, const char *email)
{
struct Address *addr = Database_get(conn, email);
if (!addr) {
// already deleted
return;
}
addr->email = NULL;
addr->name = NULL;
}
void Database_list(struct Connection *conn)
{
int i = 0;
struct Database *db = conn->db;
for (i = 0; i < Database_n_rows(conn); i++) {
struct Address *cur = &db->rows[i];
Address_print(cur);
}
}
size_t get_string_field_size()
{
size_t string_field_size = DEFAULT_MAX_DATA;
const char *string_field_size_string = getenv("EX17_MAX_DATA");
if (string_field_size_string) {
int string_field_size_int = atoi(string_field_size_string);
string_field_size = string_field_size_int;
}
return string_field_size;
}
int main(int argc, char *argv[])
{
if (argc < 3) {
die(NULL, "USAGE: ex17 <dbfile> <action> [action params]");
}
char *filename = argv[1];
char action = argv[2][0];
size_t string_field_size = get_string_field_size();
struct Connection *conn = Database_open(filename, action, string_field_size);
switch (action) {
case 'c':
Database_write(conn);
break;
case 'g':
if (argc != 4) {
die(conn, "Need an `email` to get");
}
Address_print(Database_get(conn, argv[3]));
break;
case 's':
if (argc != 5) {
die(conn, "Need a `name` and `email` to set");
}
Database_set(conn, argv[3], argv[4]);
Database_write(conn);
break;
case 'd':
if (argc != 4) {
die(conn, "Need an `email` to delete");
}
Database_delete(conn, argv[3]);
Database_write(conn);
break;
case 'l':
Database_list(conn);
break;
default:
die(conn, "Invalid action: c=create, g=get, s=set, d=del, l=list");
}
Database_close(conn);
return 0;
}

109
lcthw/ex17play.c Normal file
View File

@@ -0,0 +1,109 @@
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define NAME_SIZE 64
struct State {
int counter;
time_t updated_at;
char name[NAME_SIZE];
size_t history_entries;
};
static char *names[] = {
"boingo",
"barko",
"flip",
"dingles",
"zebra",
"horus",
"blendy",
"kitems",
"horple",
NULL,
};
char *random_name()
{
int i = 0;
int mod = 0;
for (i = 0; names[i] != NULL; i++) {
mod = i;
}
return names[random() % mod];
}
void tick(FILE *fp, int init)
{
struct State *state = malloc(sizeof(struct State));
if (!state) {
err(EXIT_FAILURE, "Memory error making State");
}
if (init == 0) {
size_t read = fread(state, sizeof(struct State), 1, fp);
fprintf(
stderr,
"Read %d \"%s\" (%lu byte(s)) from %lu\n",
state->counter, state->name, sizeof(struct State) * read, state->updated_at
);
}
time_t history[state->history_entries];
size_t histories_read = fread(&history, sizeof(time_t), state->history_entries, fp);
state->counter++;
state->updated_at = time(0);
strncpy(state->name, random_name(), NAME_SIZE);
state->history_entries++;
rewind(fp);
size_t wrote = fwrite(state, sizeof(struct State), 1, fp);
fwrite(history, sizeof(time_t), sizeof(history) / sizeof(time_t), fp);
fwrite(&state->updated_at, sizeof(time_t), 1, fp);
fprintf(
stderr,
"Wrote %d \"%s\" (%lu byte(s)) at %lu (history length=%lu)\n",
state->counter, state->name, sizeof(struct State) * wrote, state->updated_at,
state->history_entries
);
}
int main(int argc, char *argv[])
{
if (argc < 2) {
errx(EXIT_FAILURE, "Missing filename argument");
}
int random_seed = time(0);
const char *random_seed_str = getenv("RANDOM_SEED");
if (random_seed_str) {
random_seed = atoi(random_seed_str);
}
srandom(random_seed);
int init = 0;
FILE *fp = fopen(argv[1], "r+");
if (!fp) {
fp = fopen(argv[1], "w+");
init = 1;
}
if (!fp) {
err(EXIT_FAILURE, "Failed to open file");
}
tick(fp, init);
fclose(fp);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +0,0 @@
[package]
name = "spinning-square"
version = "0.1.0"
authors = [
"Dan Buch <dan@meatballhat.com>",
"TyOverby <ty@pre-alpha.com>",
"Nikita Pekin <contact@nikitapek.in>"
]
[[bin]]
name = "spinning-square"
[dependencies]
piston = "0.53.0"
piston2d-graphics = "0.43.0"
pistoncore-glutin_window = "0.70.1"
piston2d-opengl_graphics = "0.82.0"

View File

@@ -1,70 +0,0 @@
extern crate glutin_window;
extern crate graphics;
extern crate opengl_graphics;
extern crate piston;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventSettings, Events};
use piston::input::{RenderArgs, RenderEvent, UpdateArgs, UpdateEvent};
use piston::window::WindowSettings;
pub struct App {
gl: GlGraphics,
rotation: f64,
}
impl App {
fn render(&mut self, args: &RenderArgs) {
use graphics::*;
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
let square = rectangle::square(0.0, 0.0, 50.0);
let rotation = self.rotation;
let (x, y) = (args.window_size[0] / 2.0, args.window_size[1] / 2.0);
self.gl.draw(args.viewport(), |c, gl| {
clear(GREEN, gl);
let transform = c
.transform
.trans(x, y)
.rot_rad(rotation)
.trans(-25.0, -25.0);
rectangle(RED, square, transform, gl);
})
}
fn update(&mut self, args: &UpdateArgs) {
self.rotation += 2.0 * args.dt;
}
}
fn main() {
let opengl = OpenGL::V3_2;
let mut window: Window = WindowSettings::new("spinning-square", [200, 200])
.graphics_api(opengl)
.exit_on_esc(true)
.build()
.unwrap();
let mut app = App {
gl: GlGraphics::new(opengl),
rotation: 0.0,
};
let mut events = Events::new(EventSettings::new());
while let Some(e) = events.next(&mut window) {
if let Some(args) = e.render_args() {
app.render(&args);
}
if let Some(args) = e.update_args() {
app.update(&args);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
[package]
name = "sudoku"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
piston = "0.53.2"
piston2d-graphics = "0.43.0"
piston2d-opengl_graphics = "0.82.0"
pistoncore-glutin_window = "0.70.1"

View File

@@ -1,99 +0,0 @@
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Sans.
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Mono.
Copyright (c) 2014, Telefonica S.A.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -1,155 +0,0 @@
//! Game board logic.
use std::fs::read_to_string;
const SIZE: usize = 9;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Cell {
pub value: u8,
pub loaded: bool,
pub invalid: bool,
}
#[derive(Debug, PartialEq)]
pub struct Gameboard {
pub cells: [[Cell; SIZE]; SIZE],
pub completed: bool,
}
impl Gameboard {
pub fn new() -> Gameboard {
Gameboard {
cells: [[Cell::default(); SIZE]; SIZE],
completed: false,
}
}
pub fn from_cells(cells: [[u8; SIZE]; SIZE]) -> Gameboard {
let mut ret = Gameboard::new();
for (i, row) in cells.iter().enumerate() {
for (j, &col) in row.iter().enumerate() {
ret.cells[i][j] = Cell {
value: col,
loaded: col != 0,
invalid: false,
};
}
}
ret
}
pub fn char(&self, ind: [usize; 2]) -> Option<char> {
Some(match self.cells[ind[1]][ind[0]].value {
1 => '1',
2 => '2',
3 => '3',
4 => '4',
5 => '5',
6 => '6',
7 => '7',
8 => '8',
9 => '9',
_ => return None,
})
}
pub fn set(&mut self, ind: [usize; 2], val: u8) {
if !self.cells[ind[1]][ind[0]].loaded {
self.validate(ind, val);
self.cells[ind[1]][ind[0]].value = val;
}
self.completed = self
.cells
.iter()
.flatten()
.all(|cell| !cell.invalid && cell.value != 0);
}
pub fn load_sdm(filename: &str) -> Self {
let data = read_to_string(filename).expect("failed to read SDM file");
let mut cells = [[Cell::default(); SIZE]; SIZE];
let mut row = 0;
let mut col = 0;
for c in data.chars() {
if col == SIZE {
col = 0;
row += 1;
}
if let Some(v) = c.to_digit(10) {
let value = v as u8;
cells[row][col] = Cell {
value,
loaded: value != 0,
invalid: false,
};
col += 1;
}
}
Self {
cells,
completed: false,
}
}
fn validate(&mut self, ind: [usize; 2], val: u8) {
let [b, a] = ind;
for i in 0..SIZE {
if i == a {
continue;
}
if self.cells[a][i].value == val {
self.cells[a][b].invalid = true;
return;
}
}
for i in 0..SIZE {
if i == b {
continue;
}
if self.cells[i][b].value == val {
self.cells[a][b].invalid = true;
return;
}
}
let (row, col) = (a / 3, b / 3);
for i in 3 * row..3 * row + 3 {
for j in 3 * col..3 * col + 3 {
if i == a && j == b {
continue;
}
if self.cells[i][j].value == val {
self.cells[a][b].invalid = true;
return;
}
}
}
self.cells[a][b].invalid = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn load_sdm() {
let got = Gameboard::load_sdm("static/puzzle.sdm");
let want = Gameboard::from_cells([
[0, 1, 6, 4, 0, 0, 0, 0, 0],
[2, 0, 0, 0, 0, 9, 0, 0, 0],
[4, 0, 0, 0, 0, 0, 0, 6, 2],
[0, 7, 0, 2, 3, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 3],
[0, 0, 3, 0, 8, 7, 0, 4, 0],
[9, 6, 0, 0, 0, 0, 0, 0, 5],
[0, 0, 0, 8, 0, 0, 0, 0, 7],
[0, 0, 0, 0, 0, 6, 8, 2, 0],
]);
assert_eq!(got, want);
}
}

View File

@@ -1,57 +0,0 @@
//! Gameboard controller
use piston::GenericEvent;
use crate::Gameboard;
pub struct GameboardController {
pub gameboard: Gameboard,
pub selected_cell: Option<[usize; 2]>,
cursor_pos: [f64; 2],
}
impl GameboardController {
pub fn new(gameboard: Gameboard) -> GameboardController {
GameboardController {
gameboard: gameboard,
selected_cell: None,
cursor_pos: [0.0; 2],
}
}
pub fn event<E: GenericEvent>(&mut self, pos: [f64; 2], size: f64, e: &E) {
use piston::input::{Button, Key, MouseButton};
if let Some(pos) = e.mouse_cursor_args() {
self.cursor_pos = pos;
}
if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() {
let x = self.cursor_pos[0] - pos[0];
let y = self.cursor_pos[1] - pos[1];
if x >= 0.0 && x < size && y >= 0.0 && y < size {
let cell_x = (x / size * 9.0) as usize;
let cell_y = (y / size * 9.0) as usize;
self.selected_cell = Some([cell_x, cell_y]);
}
}
if let Some(Button::Keyboard(key)) = e.press_args() {
if let Some(ind) = self.selected_cell {
match key {
Key::D1 => self.gameboard.set(ind, 1),
Key::D2 => self.gameboard.set(ind, 2),
Key::D3 => self.gameboard.set(ind, 3),
Key::D4 => self.gameboard.set(ind, 4),
Key::D5 => self.gameboard.set(ind, 5),
Key::D6 => self.gameboard.set(ind, 6),
Key::D7 => self.gameboard.set(ind, 7),
Key::D8 => self.gameboard.set(ind, 8),
Key::D9 => self.gameboard.set(ind, 9),
_ => {}
}
}
}
}
}

View File

@@ -1,210 +0,0 @@
//! Gameboard view.
use graphics::character::CharacterCache;
use graphics::types::Color;
use graphics::{Context, Graphics};
use crate::gameboard_controller::GameboardController;
pub struct GameboardViewSettings {
pub position: [f64; 2],
pub size: f64,
pub background_color: Color,
pub border_color: Color,
pub board_edge_color: Color,
pub section_edge_color: Color,
pub cell_edge_color: Color,
pub board_edge_radius: f64,
pub section_edge_radius: f64,
pub cell_edge_radius: f64,
pub selected_cell_background_color: Color,
pub text_color: Color,
pub loaded_cell_background_color: Color,
pub invalid_cell_background_color: Color,
pub invalid_selected_cell_background_color: Color,
pub completed_background_color: Color,
}
impl GameboardViewSettings {
pub fn new() -> GameboardViewSettings {
GameboardViewSettings {
position: [10.0; 2],
size: 400.0,
background_color: [0.8, 0.8, 1.0, 1.0],
border_color: [0.0, 0.0, 0.2, 1.0],
board_edge_color: [0.0, 0.0, 0.2, 1.0],
section_edge_color: [0.0, 0.0, 0.2, 1.0],
cell_edge_color: [0.0, 0.0, 0.2, 1.0],
board_edge_radius: 3.0,
section_edge_radius: 2.0,
cell_edge_radius: 1.0,
selected_cell_background_color: [0.9, 0.9, 1.0, 1.0],
text_color: [0.0, 0.0, 0.1, 1.0],
loaded_cell_background_color: [1.0, 1.0, 1.0, 1.0],
invalid_cell_background_color: [1.0, 0.0, 0.0, 1.0],
invalid_selected_cell_background_color: [1.0, 0.0, 0.5, 1.0],
completed_background_color: [0.0, 1.0, 0.0, 1.0],
}
}
}
pub struct GameboardView {
pub settings: GameboardViewSettings,
}
impl GameboardView {
pub fn new(settings: GameboardViewSettings) -> GameboardView {
GameboardView { settings: settings }
}
pub fn draw<G: Graphics, C>(
&self,
controller: &GameboardController,
glyphs: &mut C,
c: &Context,
g: &mut G,
) where
C: CharacterCache<Texture = G::Texture>,
{
use graphics::{Image, Line, Rectangle, Transformed};
let ref settings = self.settings;
let board_rect = [
settings.position[0],
settings.position[1],
settings.size,
settings.size,
];
if controller.gameboard.completed {
Rectangle::new(settings.completed_background_color).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
} else {
Rectangle::new(settings.background_color).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
for i in 0..9 {
for j in 0..9 {
if controller.gameboard.cells[i][j].loaded {
color_cell(
settings,
[j, i],
settings.loaded_cell_background_color,
c,
g,
);
} else if controller.gameboard.cells[i][j].invalid {
color_cell(
settings,
[j, i],
settings.invalid_cell_background_color,
c,
g,
);
}
}
}
if let Some(ind) = controller.selected_cell {
let cell = controller.gameboard.cells[ind[1]][ind[0]];
let color = if !cell.loaded {
if !cell.invalid {
settings.selected_cell_background_color
} else {
settings.invalid_selected_cell_background_color
}
} else {
settings.loaded_cell_background_color
};
color_cell(settings, ind, color, c, g);
}
}
let text_image = Image::new_color(settings.text_color);
let cell_size = settings.size / 9.0;
for j in 0..9 {
for i in 0..9 {
if let Some(ch) = controller.gameboard.char([i, j]) {
let pos = [
settings.position[0] + i as f64 * cell_size + 15.0,
settings.position[1] + j as f64 * cell_size + 34.0,
];
if let Ok(character) = glyphs.character(34, ch) {
let ch_x = pos[0] + character.left();
let ch_y = pos[1] - character.top();
let text_image = text_image.src_rect([
character.atlas_offset[0],
character.atlas_offset[1],
character.atlas_size[0],
character.atlas_size[1],
]);
text_image.draw(
character.texture,
&c.draw_state,
c.transform.trans(ch_x, ch_y),
g,
);
}
}
}
}
let cell_edge = Line::new(settings.cell_edge_color, settings.cell_edge_radius);
let section_edge = Line::new(settings.section_edge_color, settings.section_edge_radius);
for i in 0..9 {
let x = settings.position[0] + i as f64 / 9.0 * settings.size;
let y = settings.position[1] + i as f64 / 9.0 * settings.size;
let x2 = settings.position[0] + settings.size;
let y2 = settings.position[1] + settings.size;
let vline = [x, settings.position[1], x, y2];
let hline = [settings.position[0], y, x2, y];
if (i % 3) == 0 {
section_edge.draw(vline, &c.draw_state, c.transform, g);
section_edge.draw(hline, &c.draw_state, c.transform, g);
} else {
cell_edge.draw(vline, &c.draw_state, c.transform, g);
cell_edge.draw(hline, &c.draw_state, c.transform, g);
}
}
Rectangle::new_border(settings.board_edge_color, settings.board_edge_radius).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
}
}
fn color_cell<G: Graphics>(
settings: &GameboardViewSettings,
ind: [usize; 2],
color: [f32; 4],
c: &Context,
g: &mut G,
) {
use graphics::Rectangle;
let cell_size = settings.size / 9.0;
let pos = [ind[0] as f64 * cell_size, ind[1] as f64 * cell_size];
let cell_rect = [
settings.position[0] + pos[0],
settings.position[1] + pos[1],
cell_size,
cell_size,
];
Rectangle::new(color).draw(cell_rect, &c.draw_state, c.transform, g);
}

View File

@@ -1,57 +0,0 @@
//////#![deny(missing_docs)]
//! An Sudoko please.
extern crate glutin_window;
use glutin_window::GlutinWindow;
use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings};
use piston::event_loop::{EventSettings, Events};
use piston::{EventLoop, RenderEvent, WindowSettings};
pub use crate::gameboard::Gameboard;
pub use crate::gameboard_controller::GameboardController;
pub use crate::gameboard_view::{GameboardView, GameboardViewSettings};
mod gameboard;
mod gameboard_controller;
mod gameboard_view;
fn main() {
let opengl = OpenGL::V3_2;
let settings = WindowSettings::new("Sudoku", (640, 480))
.exit_on_esc(true)
.graphics_api(opengl)
.vsync(true);
let mut window: GlutinWindow = settings.build().expect("could not create window");
let mut events = Events::new(EventSettings::new().lazy(true));
let mut gl = GlGraphics::new(opengl);
let args: Vec<_> = std::env::args().collect();
let infile = args.get(1).expect("usage: sudoku <sdm-file>");
let gameboard = Gameboard::load_sdm(infile);
let mut gameboard_controller = GameboardController::new(gameboard);
let gameboard_view_settings = GameboardViewSettings::new();
let gameboard_view = GameboardView::new(gameboard_view_settings);
let texture_settings = TextureSettings::new().filter(Filter::Nearest);
let ref mut glyphs = GlyphCache::new("assets/FiraSans-Regular.ttf", (), texture_settings)
.expect("Could not load font");
while let Some(e) = events.next(&mut window) {
gameboard_controller.event(
gameboard_view.settings.position,
gameboard_view.settings.size,
&e,
);
if let Some(args) = e.render_args() {
gl.draw(args.viewport(), |c, g| {
use graphics::clear;
clear([1.0; 4], g);
gameboard_view.draw(&gameboard_controller, glyphs, &c, g);
})
}
}
}

View File

@@ -1 +0,0 @@
517962483236847915498351762371695248654218397829734156765129834142583679983476520

View File

@@ -1 +0,0 @@
016400000200009000400000062070230100100000003003087040960000005000800007000006820

View File

@@ -0,0 +1,7 @@
module git.meatballhat.com/x/box-o-sand/protobuffery
go 1.22
toolchain go1.23.10
require google.golang.org/protobuf v1.36.6

View File

@@ -0,0 +1,6 @@
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=

View File

@@ -0,0 +1,327 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.31.0
// source: addressbook.proto
package __
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type PhoneType int32
const (
PhoneType_PHONE_TYPE_UNSPECIFIED PhoneType = 0
PhoneType_PHONE_TYPE_MOBILE PhoneType = 1
PhoneType_PHONE_TYPE_HOME PhoneType = 2
PhoneType_PHONE_TYPE_WORK PhoneType = 3
)
// Enum value maps for PhoneType.
var (
PhoneType_name = map[int32]string{
0: "PHONE_TYPE_UNSPECIFIED",
1: "PHONE_TYPE_MOBILE",
2: "PHONE_TYPE_HOME",
3: "PHONE_TYPE_WORK",
}
PhoneType_value = map[string]int32{
"PHONE_TYPE_UNSPECIFIED": 0,
"PHONE_TYPE_MOBILE": 1,
"PHONE_TYPE_HOME": 2,
"PHONE_TYPE_WORK": 3,
}
)
func (x PhoneType) Enum() *PhoneType {
p := new(PhoneType)
*p = x
return p
}
func (x PhoneType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (PhoneType) Descriptor() protoreflect.EnumDescriptor {
return file_addressbook_proto_enumTypes[0].Descriptor()
}
func (PhoneType) Type() protoreflect.EnumType {
return &file_addressbook_proto_enumTypes[0]
}
func (x PhoneType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use PhoneType.Descriptor instead.
func (PhoneType) EnumDescriptor() ([]byte, []int) {
return file_addressbook_proto_rawDescGZIP(), []int{0}
}
type Person struct {
state protoimpl.MessageState `protogen:"open.v1"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Id int32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"`
Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
Phones []*Person_PhoneNumber `protobuf:"bytes,4,rep,name=phones,proto3" json:"phones,omitempty"`
LastUpdated *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=last_updated,json=lastUpdated,proto3" json:"last_updated,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Person) Reset() {
*x = Person{}
mi := &file_addressbook_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Person) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Person) ProtoMessage() {}
func (x *Person) ProtoReflect() protoreflect.Message {
mi := &file_addressbook_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Person.ProtoReflect.Descriptor instead.
func (*Person) Descriptor() ([]byte, []int) {
return file_addressbook_proto_rawDescGZIP(), []int{0}
}
func (x *Person) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Person) GetId() int32 {
if x != nil {
return x.Id
}
return 0
}
func (x *Person) GetEmail() string {
if x != nil {
return x.Email
}
return ""
}
func (x *Person) GetPhones() []*Person_PhoneNumber {
if x != nil {
return x.Phones
}
return nil
}
func (x *Person) GetLastUpdated() *timestamppb.Timestamp {
if x != nil {
return x.LastUpdated
}
return nil
}
type AddressBook struct {
state protoimpl.MessageState `protogen:"open.v1"`
People []*Person `protobuf:"bytes,1,rep,name=people,proto3" json:"people,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AddressBook) Reset() {
*x = AddressBook{}
mi := &file_addressbook_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AddressBook) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddressBook) ProtoMessage() {}
func (x *AddressBook) ProtoReflect() protoreflect.Message {
mi := &file_addressbook_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AddressBook.ProtoReflect.Descriptor instead.
func (*AddressBook) Descriptor() ([]byte, []int) {
return file_addressbook_proto_rawDescGZIP(), []int{1}
}
func (x *AddressBook) GetPeople() []*Person {
if x != nil {
return x.People
}
return nil
}
type Person_PhoneNumber struct {
state protoimpl.MessageState `protogen:"open.v1"`
Number string `protobuf:"bytes,1,opt,name=number,proto3" json:"number,omitempty"`
Type PhoneType `protobuf:"varint,2,opt,name=type,proto3,enum=tutorial.PhoneType" json:"type,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Person_PhoneNumber) Reset() {
*x = Person_PhoneNumber{}
mi := &file_addressbook_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Person_PhoneNumber) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Person_PhoneNumber) ProtoMessage() {}
func (x *Person_PhoneNumber) ProtoReflect() protoreflect.Message {
mi := &file_addressbook_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Person_PhoneNumber.ProtoReflect.Descriptor instead.
func (*Person_PhoneNumber) Descriptor() ([]byte, []int) {
return file_addressbook_proto_rawDescGZIP(), []int{0, 0}
}
func (x *Person_PhoneNumber) GetNumber() string {
if x != nil {
return x.Number
}
return ""
}
func (x *Person_PhoneNumber) GetType() PhoneType {
if x != nil {
return x.Type
}
return PhoneType_PHONE_TYPE_UNSPECIFIED
}
var File_addressbook_proto protoreflect.FileDescriptor
const file_addressbook_proto_rawDesc = "" +
"\n" +
"\x11addressbook.proto\x12\btutorial\x1a\x1fgoogle/protobuf/timestamp.proto\"\x87\x02\n" +
"\x06Person\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12\x0e\n" +
"\x02id\x18\x02 \x01(\x05R\x02id\x12\x14\n" +
"\x05email\x18\x03 \x01(\tR\x05email\x124\n" +
"\x06phones\x18\x04 \x03(\v2\x1c.tutorial.Person.PhoneNumberR\x06phones\x12=\n" +
"\flast_updated\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\vlastUpdated\x1aN\n" +
"\vPhoneNumber\x12\x16\n" +
"\x06number\x18\x01 \x01(\tR\x06number\x12'\n" +
"\x04type\x18\x02 \x01(\x0e2\x13.tutorial.PhoneTypeR\x04type\"7\n" +
"\vAddressBook\x12(\n" +
"\x06people\x18\x01 \x03(\v2\x10.tutorial.PersonR\x06people*h\n" +
"\tPhoneType\x12\x1a\n" +
"\x16PHONE_TYPE_UNSPECIFIED\x10\x00\x12\x15\n" +
"\x11PHONE_TYPE_MOBILE\x10\x01\x12\x13\n" +
"\x0fPHONE_TYPE_HOME\x10\x02\x12\x13\n" +
"\x0fPHONE_TYPE_WORK\x10\x03B\x03Z\x01.b\x06proto3"
var (
file_addressbook_proto_rawDescOnce sync.Once
file_addressbook_proto_rawDescData []byte
)
func file_addressbook_proto_rawDescGZIP() []byte {
file_addressbook_proto_rawDescOnce.Do(func() {
file_addressbook_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_addressbook_proto_rawDesc), len(file_addressbook_proto_rawDesc)))
})
return file_addressbook_proto_rawDescData
}
var file_addressbook_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_addressbook_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_addressbook_proto_goTypes = []any{
(PhoneType)(0), // 0: tutorial.PhoneType
(*Person)(nil), // 1: tutorial.Person
(*AddressBook)(nil), // 2: tutorial.AddressBook
(*Person_PhoneNumber)(nil), // 3: tutorial.Person.PhoneNumber
(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
}
var file_addressbook_proto_depIdxs = []int32{
3, // 0: tutorial.Person.phones:type_name -> tutorial.Person.PhoneNumber
4, // 1: tutorial.Person.last_updated:type_name -> google.protobuf.Timestamp
1, // 2: tutorial.AddressBook.people:type_name -> tutorial.Person
0, // 3: tutorial.Person.PhoneNumber.type:type_name -> tutorial.PhoneType
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_addressbook_proto_init() }
func file_addressbook_proto_init() {
if File_addressbook_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_addressbook_proto_rawDesc), len(file_addressbook_proto_rawDesc)),
NumEnums: 1,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_addressbook_proto_goTypes,
DependencyIndexes: file_addressbook_proto_depIdxs,
EnumInfos: file_addressbook_proto_enumTypes,
MessageInfos: file_addressbook_proto_msgTypes,
}.Build()
File_addressbook_proto = out.File
file_addressbook_proto_goTypes = nil
file_addressbook_proto_depIdxs = nil
}

View File

@@ -0,0 +1,32 @@
syntax = "proto3";
package tutorial;
import "google/protobuf/timestamp.proto";
option go_package = "git.meatballhat.com/x/box-o-sand/protobuffery/pb";
message Person {
string name = 1;
int32 id = 2;
string email = 3;
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
google.protobuf.Timestamp last_updated = 5;
}
enum PhoneType {
PHONE_TYPE_UNSPECIFIED = 0;
PHONE_TYPE_MOBILE = 1;
PHONE_TYPE_HOME = 2;
PHONE_TYPE_WORK = 3;
}
message AddressBook {
repeated Person people = 1;
}

View File

@@ -0,0 +1,2 @@
ipython
pygame-ce

33
sweeptag/sweeptag.py Normal file
View File

@@ -0,0 +1,33 @@
import pygame
def main():
pygame.init()
screen = pygame.display.set_mode((640, 480))
running = True
font = pygame.Font(None, 80)
title_text = "Sweeptag"
#title_size = font.size(title_text)
title_rendered = font.render(title_text, False, (255, 255, 255), (0, 0, 0))
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
running = False
else:
print(f"EVENT: {event}")
screen.fill((250, 150, 0))
screen.blit(title_rendered, (20, 20))
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,121 @@
package main
import (
"bufio"
"context"
"errors"
"fmt"
"log"
"net"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
)
func main() {
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
addr := ":7765"
if len(os.Args) > 1 {
addr = os.Args[1]
}
poolSize := 10
if len(os.Args) > 2 {
if v, err := strconv.ParseInt(os.Args[2], 10, 64); err != nil {
log.Fatal(err)
} else {
poolSize = int(v)
}
}
log.Printf("listening at %v (workers=%v)", addr, poolSize)
listener, err := net.Listen("tcp", addr)
if err != nil {
log.Fatal(err)
}
go func() {
<-ctx.Done()
if err := listener.Close(); err != nil {
log.Printf("failed to close listener: %v", err)
}
}()
workers := []*worker{}
requests := make(chan net.Conn)
for i := 0; i < poolSize; i++ {
w := &worker{id: i, r: requests}
go w.Start(ctx)
workers = append(workers, w)
}
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Microsecond):
continue
default:
log.Printf("entering accept condition")
conn, err := listener.Accept()
if err != nil {
if errors.Is(err, net.ErrClosed) {
return
}
log.Printf("failed to accept connection: %v", err)
continue
}
conn.SetDeadline(time.Now().Add(5 * time.Second))
log.Printf("accepted connection %v", conn.RemoteAddr())
requests <- conn
}
}
}
type worker struct {
id int
r chan net.Conn
}
func (w *worker) Start(ctx context.Context) {
log.Printf("[worker %v] starting loop", w.id)
for {
select {
case <-ctx.Done():
log.Printf("[worker %v] exiting", w.id)
return
case conn := <-w.r:
log.Printf("[worker %v] handling connection %v", w.id, conn.RemoteAddr())
fmt.Fprintf(conn, "ITS ECHO TIME\n")
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
req := strings.TrimSpace(scanner.Text())
if req == "BYE" {
fmt.Fprintf(conn, "BYE BYE NOW\n")
break
}
fmt.Fprintf(conn, "YOU SAID: %v\n", req)
}
if err := conn.Close(); err != nil {
log.Printf("[worker %v] failed to echo: %v", w.id, err)
}
}
}
}

View File

@@ -0,0 +1,3 @@
module git.meatballhat.com/x/box-o-sand/tcp-challenge/server
go 1.21.4