initial commit

This commit is contained in:
ben committed 2026-05-17 12:19:19 -07:00
commit fbd12d4a8e
320 files changed
+124298

No files matched your search

@@ -0,0 +1,3 @@
#rs/class/ad300 #rs/assignment
- - -
I'm always interested in new technology and I have explored using many new AI tools in the last few years. I don't find most of them very helpful (image generation, video generation, music generation etc.) but I do use general LLMs like ChatGPT semi-regularly. One of the things I use tools like ChatGPT for is researching obscure topics that might be hard to find otherwise on the internet. It can often be a good starting place to get an overview of a topic that doesn't have much information available. This means that before I dive into obscure forum posts and comments I can have a general understanding of something even if there aren't any summary or general articles online. I have also relatively recently been exploring topics like Linux usage and managing home servers that often have very specific problems that likely won't easily find solutions for online. ChatGPT can often point me in the right direction and at least tell me what is going wrong so I can better find answers on the internet. I haven't actually used generative AI much for directly coding with tools such as Copilot or Cursor. I'll occasionally ask something like ChatGPT for advice when something is going wrong that I can't find any solutions to anywhere else, but I don't have much experience with the more specialized tools. With the use cases I have I have found that ChatGPT is often wrong in at least one part of its response and I always verify everything and will use it more as a starting point rather than an end. Instead of copy-pasting and using the code it gives me (which almost never works) I use it to get a more general understanding of the topic so that I can better do it myself or look online at more accurate sources. During this course I don't plan on using generative AI much, although if I am struggling with something more complex I might ask it for direction. I have many concerns with AI usage in an academic setting, mostly around others using it to replace their learning rather than enhancing it. In the end there is not much I can do about that though and I'll focus on making sure that I learn the content and understand it without having to lean on generative AI and rather using it to enhance my learning or productivity.
@@ -0,0 +1,60 @@
#rs/class/ad300 #rs/discussion
- - -
https://github.com/search?q=repo%3Arunelite%2Frunelite+comparable&type=code
https://github.com/runelite/runelite/blob/e25afded97ae8c4bcc8754e60511de8563060b67/runelite-api/src/main/java/net/runelite/api/Nameable.java#L30
https://github.com/runelite/runelite/blob/e25afded97ae8c4bcc8754e60511de8563060b67/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginSearch.java#L49
I chose runelite, an open-source Runescape client written in Java, as my project. Here is a link: https://github.com/mojo626/AD300-ComparableInterface
The first use of a comparator that I found was the comparator [AlphaModelComparator](https://github.com/runelite/runelite/blob/c7ebe1f362e1cd794813afc0be848d09f01f3d16/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java#L496)
```java
static class AlphaModelComparator implements Comparator<AlphaModel>
{
int zx, zz;
int cx, cy, cz;
@Override
public int compare(AlphaModel o1, AlphaModel o2)
{
return Integer.compare(z(o2), z(o1));
}
private int z(AlphaModel m)
{
final int mx = (m.x + ((zx - m.zofx) << 10));
final int mz = (m.z + ((zz - m.zofz) << 10));
return (mx - cx) * (mx - cx) +
(m.y - cy) * (m.y - cy) +
(mz - cz) * (mz - cz);
}
}
```
I don't know exactly how this comparator works but it is for a plugin that shifts rendering to the GPU and I assume that this is used to sort the faces of models in game to make sure that they are being rendered in the correct order. A comparator is useful here because sorting faces based on z position to the camera is difficult, especially with things such as culling or overlapping faces, and a comparator allows easy definition of a custom sorting function.
The only use of comparable that I could find was the [Namable](https://github.com/runelite/runelite/blob/c7ebe1f362e1cd794813afc0be848d09f01f3d16/runelite-api/src/main/java/net/runelite/api/Nameable.java#L30) interface.
```java
/**
* Represents a chat entity that has a name.
*/
public interface Nameable extends Comparable<Nameable>
{
/**
* The name of the player.
*
* @return the name
*/
String getName();
/**
* The previous name the player had.
*
* @return the previous name
*/
String getPrevName();
}
```
Since this interface represents something in chat, it is useful to define a custom sorting function. It might be helpful to sort the entities by name, but it could also be used to sort them into something like categories first and then alphabetically within them and using a comparable allows for easy changing and modularity of the sorting function.
Comparator seems to be more useful in this project since for many of the objects there will likely be multiple ways that they can be sorted and writing several comparators allows for this. I didn't notice any particular best practices or patterns in the use of comparators or comparables since there were not very many examples in the project that I chose. For the AlphaModelComparator class it seems strange that there are local variables that are populated in the comparator after creating it and before sorting. Maybe these variables (zx, cx etc.) could be added to the objects being compared to make the code a little more readable?
@@ -0,0 +1,55 @@
#rs/class/ad300 #rs/discussion
- - -
https://github.com/libgdx/libgdx/?tab=readme-ov-file
I chose libGDX, an open source cross-platform game development framework developed in Java. Here is a link: https://github.com/libgdx/libgdx/?tab=readme-ov-file
The first example of a generic that I chose was in the [TimSort](https://github.com/libgdx/libgdx/blob/91caf85c5701edb297495e38c356bc7ab9db1131/gdx/src/com/badlogic/gdx/utils/TimSort.java#L178) class that implements a better version of merge sort for sorting arrays in the engine. Using a generic for the type to sort helps because it allows an array with any type to be sorted and reduces the amount of code that needs to be written since a different method does not need to be written for each type.
```java
static <T> void sort (T[] a, Comparator<? super T> c) {
sort(a, 0, a.length, c);
}
static <T> void sort (T[] a, int lo, int hi, Comparator<? super T> c) {
if (c == null) {
Arrays.sort(a, lo, hi);
return;
}
...
...
...
}
```
Overall, the use of a generic here makes the code much more flexible and readable since there can be just one sort method that implements all of the logic for every type that needs to be sorted. There likely isn't much of a performance affect other than that less code needs to be written.
The second example that I chose was the [Path interface](https://github.com/libgdx/libgdx/blob/91caf85c5701edb297495e38c356bc7ab9db1131/gdx/src/com/badlogic/gdx/math/Path.java#L21). This interface defines a path of type T and functions that must be implemented for this path such as finding the derivative or value at a point.
```java
public interface Path<T> {
T derivativeAt (T out, float t);
/** @return The value of the path at t where 0<=t<=1 */
T valueAt (T out, float t);
/** @return The approximated value (between 0 and 1) on the path which is closest to the specified value. Note that the
* implementation of this method might be optimized for speed against precision, see {@link #locate(Object)} for a more
* precise (but more intensive) method. */
float approximate (T v);
/** @return The precise location (between 0 and 1) on the path which is closest to the specified value. Note that the
* implementation of this method might be CPU intensive, see {@link #approximate(Object)} for a faster (but less
* precise) method. */
float locate (T v);
/** @param samples The amount of divisions used to approximate length. Higher values will produce more precise results, but
* will be more CPU intensive.
* @return An approximated length of the spline through sampling the curve and accumulating the euclidean distances between the
* sample points. */
float approxLength (int samples);
}
```
Using a generic for the type of path here helps improve code flexibility in case the implementation of paths changes in the future. Maybe instead of using a Vector to define the point on a path it will instead need to use a new Point class. Instead of having to rewrite all of this code any definitions of a path can just use the Point class instead of the Vector class since Path is generic.
Before this week I didn't know a lot about generics in Java except for using them when defining things like an ArrayList. I had never written a generic class or method and now I feel like I better understand how generics work better in Java. In the future, I'll probably use generics when I need to write more broad helper functions like sorting algorithms. This allows it to work on any type and can help to reduce the amount of code I have to write and make it more readable to others. Generics can also be very helpful for future proofing code more since if a new class is used instead of having to change the algorithms it can just be swapped into the generic.
@@ -0,0 +1,71 @@
#rs/class/ad300 #rs/discussion
- - -
For this discussion I looked at [Mindustry](https://github.com/Anuken/Mindustry). Mindustry is a tower defense factory building game that is open source and written in java. I mostly found examples of interfaces for parts of the code where it is helpful to have multiple different implementations of one thing for flexibility or future changes. This included world generation, goals and weapon logic.
One of the first examples I found was the interface [Objective](https://github.com/Anuken/Mindustry/blob/2ad41a904753a47f6fb1a7b64dbea46204ce207e/core/src/mindustry/game/Objectives.java#L141).
```java
/** Defines a specific objective for a game. */
public interface Objective{
/** @return whether this objective is met. */
boolean complete();
/** @return the string displayed when this objective is completed, in imperative form.
* e.g. when the objective is 'complete 10 waves', this would display "complete 10 waves". */
String display();
/** Build a display for this zone requirement.*/
default void build(Table table){
}
}
```
This interface represents a generic objective in the game to ensure that they all have certain methods such as "complete" and "display." Different objectives can implement this interface to allow a variety of different objectives to be interchangeable since they are required to have the same base methods.
One example of a class that implements the Objective interface is the Research class.
```java
public static class Research implements Objective{
public UnlockableContent content;
public Research(UnlockableContent content){
this.content = content;
}
protected Research(){}
@Override
public boolean complete(){
return content.unlockedHost();
}
@Override
public String display(){
return Core.bundle.format("requirement.research",
//TODO broken for multi tech nodes.
(content.techNode == null || content.techNode.parent == null || content.techNode.parent.content.unlockedHost()) ?
(content.emoji() + " " + content.localizedName) : "???");
}
@Override
public String toString(){
return "research: " + content;
}
}
```
This implements its own logic for checking the progress and displaying the task in the game. This interface is beneficial since there are many different possible objectives and using an interface like this ensures that they all implement required methods to check their status in game and make sure that they all work together well.
A second example that I found was the [WorldGenerator](https://github.com/Anuken/Mindustry/blob/2ad41a904753a47f6fb1a7b64dbea46204ce207e/core/src/mindustry/maps/generators/WorldGenerator.java#L5) interface.
```
public interface WorldGenerator{
void generate(Tiles tiles, WorldParams params);
/** Do not modify tiles here. This is only for specialized configuration. */
default void postGenerate(Tiles tiles){}
}
```
While this isn't a very complex interface, it is still important since it allows for different world generators to easily be added and interchanged. It defines a generate method with specific inputs. The [BasicGenerator](https://github.com/Anuken/Mindustry/blob/2ad41a904753a47f6fb1a7b64dbea46204ce207e/core/src/mindustry/maps/generators/BasicGenerator.java#L18) is an example of a class that implements WorldGenerator. It has the generate method as well as other methods that assist in the world generation. An interface is helpful here because a different generator could be written that implements WorldGenerator and it would be easy to interchange the BasicGenerator with the new one.
https://github.com/Anuken/Mindustry/blob/2ad41a904753a47f6fb1a7b64dbea46204ce207e/core/src/mindustry/type/AmmoType.java#L7
https://github.com/Anuken/Mindustry/blob/2ad41a904753a47f6fb1a7b64dbea46204ce207e/core/src/mindustry/maps/generators/WorldGenerator.java#L5
@@ -0,0 +1,15 @@
#rs/class/ad300 #rs/discussion
- - -
1. My name is Benjamin, but I usually go by Ben.
2. My pronouns are he/him
3. I have lived in Seattle for my whole life.
4. Over the last few years I have enjoyed doing indoor bouldering. There is a climbing gym near my house and I will often go with friends as well.
5. I hope to build my programming skills and gain a better foundation than I currently have.
6. I recently read the book The Fragile Threads of Power by V.E. Schwab and enjoyed it. It is the first book in a second trilogy and I read the first one a while ago and didn't realize that there was a new book out.
7. I am definitely a night owl since I don't love waking up early and usually get most of my work done in the evening.
8. I would love to go back to Japan; I went with my family right before COVID and it was a lot of fun and I would love to spend some more time there since there is so much to explore.
9. I haven't done it in a while but I can probably still unicycle.
10. A bit of both, I have had both cats and dogs but I prefer dogs to cats.
11. I am usually more of a podcast person and I like to listen to podcasts like The Daily and Hardfork.
12. It's not very interesting but pizza is always a great option.
13. None in particular.
@@ -0,0 +1,3 @@
#rs/class/ad300 #rs/discussion
- - -
One example that I think works pretty well is Pokémon. We can have a base Pokémon class with an "electric type Pokémon" subclass and then "Pikachu" subclass for a specific Pokémon. A base Pokémon superclass might have variables like number and name that subclasses will inherit. The "electric type Pokémon" subclass might add a "possible moves" field for electric type moves that the Pokémon can learn. The Pikachu class might then have specific height, weight, abilities, and could override functions like "make sound" or "get information." You can use the "electric type Pokémon" to reference any electric type Pokémon even though they might have different moves or abilities showing polymorphism. Each Pokémon class can also include functions for abilities and moves as well as traits like height, weight, and name for encapsulation. The base Pokémon class could have a "baseMove" abstract method that each subclass has to implement showcasing abstraction.
@@ -0,0 +1,72 @@
#rs/class/ad300 #rs/discussion
- - -
https://github.com/jpcsp/jpcsp ?
The Java stream api allows you to process lists of objects in a easy and functional way. Collections in Java are used for storing information in memory while streams process lists of objects in a pipeline fashion by chaining together functions and piping the result into the next one. Streams need a terminal operation at the end to do something with the data or nothing will happen as a result. Some of these terminal operations include collect to get a list, forEach to iterate through the elements and reduce to reduce the elements to a single value. To perform operations on a stream intermediate operations are used such as map to apply a given function to each element, filter to select elements and sorted to sort the stream. Streams in java are not executed until their terminal operation is invoked which is called lazy evaluation.
The project that I chose to look at for this assignment is [OpenRocket](https://github.com/openrocket/openrocket), which is open-source simulation software for model rockets.
The first example that I found of the use of the stream api was to get all of the children of a rocket component. The last example that I found of the stream api was in updating the motor state of the rockets. https://github.com/openrocket/openrocket/blob/unstable/core/src/main/java/info/openrocket/core/rocketcomponent/Rocket.java#L867
```java
/**
* Returns all descendants of the specified component.
*
* @param component Component to query
* @return All descendants
* @apiNote Returns an empty set if the component does not have children.
*/
private Set<RocketComponent> getDescendants(RocketComponent component) {
Objects.requireNonNull(component);
var result = new LinkedHashSet<RocketComponent>();
var queue = new ArrayDeque<>(component.getChildren());
while (!queue.isEmpty()) {
var node = queue.pop();
result.add(node);
node.getChildren().stream().filter(c -> !result.contains(c)).forEach(queue::add);
}
return result;
}
```
This function takes all of the children of a specified component and then adds them to a queue. It then loops through that queue and adds each child to it as well as any children it has. It uses a stream to ensure that nodes are not added twice using the filter method. It then uses the forEach terminal method to add each node not already in the queue to the queue.
Another example that I found of the stream api is in a function to format flight data. https://github.com/openrocket/openrocket/blob/2719a75a0c9fdd4b84e7eda53e80706dca6f9db5/swing/src/main/java/info/openrocket/swing/gui/simulation/AerodynamicLookupDialog.java#L640
```java
static String formatLookupSummary(Translator translator, MachAoALookup table) {
String columns = table.getValueColumns().stream()
.map(name -> name.toUpperCase(Locale.ROOT))
.collect(java.util.stream.Collectors.joining(", "));
String machMin = formatDouble(table.getMinMach());
String machMax = formatDouble(table.getMaxMach());
if (table.hasAoA()) {
String aoaMin = formatDouble(table.getMinAoA());
String aoaMax = formatDouble(table.getMaxAoA());
return String.format(translator.get("AerodynamicLookupDialog.summaryWithAoA"),
machMin, machMax, aoaMin, aoaMax, columns);
}
return String.format(translator.get("AerodynamicLookupDialog.summaryNoAoA"),
machMin, machMax, columns);
}
```
The stream api is used here to format all of the data columns to make them all upper case and join them with commas in between. This is helpful since it is a much more concise and readable method than using a loop and concatenating strings.
The last example that I found of the stream api was in updating the motor state of the rockets. https://github.com/openrocket/openrocket/blob/unstable/core/src/main/java/info/openrocket/core/rocketcomponent/Rocket.java#L867
```java
private void updateMotorState() {
Rocket rocket = document.getRocket();
boolean newHasValidConfig = rocket != null &&
rocket.getIds().stream().anyMatch(rocket::hasMotors);
if (newHasValidConfig == hasValidConfig) {
return;
}
hasValidConfig = newHasValidConfig;
cardLayout.show(cardPanel, hasValidConfig ? CARD_TABLE : CARD_HELP);
}
```
Stream is used here to make sure that all of the flight configs for the rocket have valid motors. The stream api is used to make this much more compact and readable and contains it to one line.
In this project it seems like the stream api is mostly used to make the code more concise and readable and as an alternative to loops that might take up more space. It is much easier to understand a forEach function than a loop through an array and it takes up a lot less space. In future projects I will likely use it in a similar way to make my code more understandable to others and take up less space.