This commit is contained in:
nik
2026-05-20 16:20:45 -07:00
commit 007710b5a8
91 changed files with 450401 additions and 0 deletions

41
Bomb.java Normal file
View File

@@ -0,0 +1,41 @@
import java.io.*;
public class Bomb {
public static final int START = 124;
public static final int CODE_LENGTH = 6;
public static void defuse(String code) throws Exception {
if (code.length() != CODE_LENGTH) {
throw new Bomb.Explosion();
}
String curr = Integer.toHexString(CODE_LENGTH);
for (int i = 1; i < CODE_LENGTH; i++) {
curr = scramble(curr);
if (curr.charAt(1) != code.charAt(i)) {
throw new Bomb.Explosion();
}
}
}
private static String scramble(String secret) {
return Integer.toHexString(Integer.parseInt(secret, 16) * 0xA9 / 0b010011);
}
public static void main(String[] args) throws Exception {
System.out.println();
System.out.println("Bomb armed and ready for defusal.");
System.setOut(new PrintStream(new OutputStream() { public void write(int b) {} }));
// defuse("21d74"); // TODO: Find the code, hurry!
defuse("000000");
System.setOut(SYSTEM_OUT);
System.out.println("Bomb defused! You saved the day!");
System.out.println();
}
public static final PrintStream SYSTEM_OUT = System.out;
private static class Explosion extends RuntimeException {
public Explosion() { super("BOOM"); }
}
}

21
c1/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,21 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Current File",
"request": "launch",
"mainClass": "${file}"
},
{
"type": "java",
"name": "Client",
"request": "launch",
"mainClass": "Client",
"projectName": "c1_b4eddbb1"
}
]
}

View File

@@ -0,0 +1,47 @@
import java.util.*;
/**
* A strategy game where all players have perfect information and no theme
* or narrative around gameplay.
*/
public abstract class AbstractStrategyGame {
/**
* Constructs and returns a String describing how to play the game. Should include
* any relevant details on how to interpret the game state as returned by toString(),
* how to make moves, the game end condition, and how to win.
*/
public abstract String instructions();
/**
* Constructs and returns a String representation of the current game state.
* This representation should contain all information that should be known to
* players at any point in the game, including board state (if any) and scores (if any).
*/
public abstract String toString();
/**
* Returns true if the game has ended, and false otherwise.
*/
public boolean isGameOver() {
return getWinner() != -1;
}
/**
* Returns the index of the player who has won the game,
* or -1 if the game is not over.
*/
public abstract int getWinner();
/**
* Returns the index of the player who will take the next turn.
* If the game is over, returns -1.
*/
public abstract int getNextPlayer();
/**
* Takes input from the parameter to specify the move the player
* with the next turn wishes to make, then executes that move.
* If any part of the move is illegal, throws an IllegalArgumentException.
*/
public abstract void makeMove(Scanner input);
}

36
c1/Client.java Normal file
View File

@@ -0,0 +1,36 @@
import java.util.*;
public class Client {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
AbstractStrategyGame game = new ConnectFour("dude", "bro");
System.out.println(game.instructions());
System.out.println();
while (!game.isGameOver()) {
System.out.println(game);
System.out.printf("Player %d's turn.\n", game.getNextPlayer());
try {
game.makeMove(console);
} catch (IllegalArgumentException ex) {
System.out.println("**Illegal move: " + ex.getMessage());
}
/**
* Note - the above structure is a try/catch, which is something
* we've included to help deal with the IllegalArgumentExceptions
* in your abstract strategy game!
* We want to remind you that try/catch is a forbidden feature in 123,
* so you SHOULD NOT INCLUDE IT in any code you submit (other than this file)!
* Please see the Code Quality Guide for more info on this.
*/
}
System.out.println(game);
int winner = game.getWinner();
if (winner > 0) {
System.out.printf("Player %d wins!\n", winner);
} else {
System.out.println("It's a tie!");
}
}
}

117
c1/ConnectFour.java Normal file
View File

@@ -0,0 +1,117 @@
// Nik Johnson
// 4-15-2024
// TA: Zachary Bi
import java.util.*;
public class ConnectFour extends AbstractStrategyGame {
private char[][] board;
private int currentPlayer;
private String[] players;
private List<Character> playerSymbols;
public ConnectFour(String player1, String player2) {
this.board = new char[6][7];
this.players = new String[2];
this.playerSymbols = new ArrayList<>();
this.currentPlayer = 0;
players[0] = player1;
players[1] = player2;
playerSymbols.add('X');
playerSymbols.add('O');
// initialize empty board
for (int i = 0; i < board.length; i++) {
for (int n = 0; n < board[i].length; n++) {
board[i][n] = ' ';
}
}
}
public String instructions() {
String output = "";
output += "Welcome to Connect Four! This game consists of a 7x6 grid, each column of";
output += " which you may choose to 'drop' a disc down by inputting numbers 1-7.\n";
output += "Player 1's symbol is 'X', Player 2's is 'O'\n";
output += "To win, you must place 4 of your discs in a row, vertically or horizontally.\n";
output += "Have fun!";
return output;
}
// constructs game board in readable format
// no parameters
// returns formatted representation of game board
public String toString() {
String output = "";
for (char[] row : board) {
for (char loc : row) {
output += "[" + loc + "]";
}
output += "\n";
}
return output;
}
public int getWinner() {
// rows
for (int i = 0; i < board.length; i++) {
for (int j = 0; j <= board[i].length - 4; j++) {
char symbol = board[i][j];
if (symbol != ' ' && board[i][j + 1] == symbol && board[i][j + 2] == symbol && board[i][j + 3] == symbol) {
return playerSymbols.indexOf(symbol) + 1;
}
}
}
// columns
for (int j = 0; j < board[0].length; j++) {
for (int i = 0; i <= board.length - 4; i++) {
char symbol = board[i][j];
if (symbol != ' ' && board[i + 1][j] == symbol && board[i + 2][j] == symbol && board[i + 3][j] == symbol) {
return playerSymbols.indexOf(symbol) + 1;
}
}
}
return -1;
}
public int getNextPlayer() {
return this.currentPlayer + 1;
}
public void makeMove(Scanner input) {
System.out.println(players[getNextPlayer() - 1] + ": Choose a column (1-7)");
int col = Integer.parseInt(input.next()) - 1;
if (col < 0 || col > 6) {
throw new IllegalArgumentException("selected column out of bounds");
}
boolean notPlaced = true;
for (int n = board.length - 1; n >= 0 && notPlaced; n--) {
if (board[n][col] == ' ') {
if (currentPlayer == 0) {
board[n][col] = 'X';
} else if (currentPlayer == 1) {
board[n][col] = 'O';
}
notPlaced = false;
}
}
if (currentPlayer == 0) {
currentPlayer = 1;
} else if (currentPlayer == 1) {
currentPlayer = 0;
}
}
}

53
c1/Testing.java Normal file
View File

@@ -0,0 +1,53 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
public class Testing {
@Test
@DisplayName("EXAMPLE TEST CASE - Small TicTacToe Example")
public void firstCaseTest() {
AbstractStrategyGame g = new TicTacToe();
// You can add optional error messages that will be displayed if a test fails
assertEquals(1, g.getNextPlayer(), "Player 1 not next player after construction");
assertEquals(-1, g.getWinner(), "Winner incorrectly declared after construction");
assertFalse(g.isGameOver(), "Game over immediately after construction");
// Weird way we're going to make moves - make our own scanners NOT
// connected to System.in. Since we can make Scanners over strings this will
// work the exact way and allow us to control input!
g.makeMove(new Scanner("0 0"));
assertEquals(2, g.getNextPlayer(), "Player 2 not next player after a single move");
assertEquals(-1, g.getWinner(), "Winner incorrectly declared after a single move");
assertFalse(g.isGameOver(), "Game over immediately after construction");
assertThrows(IllegalArgumentException.class, () -> {
// -1 is an illegal move so our code should throw an IllegalArgumentException
g.makeMove(new Scanner("-1 2"));
}, "IllegalArgumentException not thrown for illegal move");
}
@Test
@DisplayName("EXAMPLE TEST CASE - Large TicTacToe Example")
public void secondCaseTest() {
// You definitely don't have to get this fancy in your tests!
AbstractStrategyGame g = new TicTacToe();
// Going to play a whole game where 1 plays in first row, 2 plays in second row
// No optional error messages - up to you if you want your code to be easier to debug!
for (int i = 0; i < 5; i++) {
int player = (i % 2) + 1;
assertEquals(player, g.getNextPlayer());
assertFalse(g.isGameOver());
int col = i / 2;
g.makeMove(new Scanner(player + " " + col));
}
// At this point, 5 moves have been played, player 1 should have three in a row and
// player 2 should have two
assertTrue(g.isGameOver());
assertEquals(1, g.getWinner());
assertEquals(-1, g.getNextPlayer());
}
}

132
c1/TicTacToe.java Normal file
View File

@@ -0,0 +1,132 @@
// **THIS IS AN EXAMPLE IMPLEMENTATION!**
// Brett Wortzman
// CSE 123
// C0: Abstract Strategy Games
//
// A class to represent a game of tic-tac-toe that implements the
// AbstractStrategyGame interface.
import java.util.*;
public class TicTacToe extends AbstractStrategyGame {
private char[][] board;
private boolean isXTurn;
// Constructs a new TicTacToe game.
public TicTacToe() {
board = new char[][]{{'-', '-', '-'},
{'-', '-', '-'},
{'-', '-', '-'}};
isXTurn = true;
}
// Returns whether or not the game is over.
public boolean isGameOver() {
return getWinner() >= 0;
}
// Returns the index of the winner of the game.
// 1 if player 1 (X), 2 if player 2 (O), 0 if a tie occurred,
// and -1 if the game is not over.
public int getWinner() {
for (int i = 0; i < board.length; i++) {
// check row i
if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] != '-') {
return board[i][0] == 'X' ? 1 : 2;
}
// check col i
if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != '-') {
return board[0][i] == 'X' ? 1 : 2;
}
}
// check diagonals
if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] != '-') {
return board[0][0] == 'X' ? 1 : 2;
}
if (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] != '-') {
return board[0][2] == 'X' ? 1 : 2;
}
// check for tie
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if (board[i][j] == '-') {
// unfilled space; game not over
return -1;
}
}
}
// it's a tie!
return 0;
}
// Returns the index of which player's turn it is.
// 1 if player 1 (X), 2 if player 2 (O), -1 if the game is over
public int getNextPlayer() {
if (isGameOver()) {
return -1;
}
return isXTurn ? 1 : 2;
}
// Given the input, places an X or an O where
// the player specifies.
// Throws an IllegalArgumentException if the position is
// invalid, whether that be out of bounds or already occupied.
// Board bounds are [0, 2] for both rows and cols.
public void makeMove(Scanner input) {
char currPlayer = isXTurn ? 'X' : 'O';
System.out.print("Row? ");
int row = input.nextInt();
System.out.print("Column? ");
int col = input.nextInt();
makeMove(row, col, currPlayer);
isXTurn = !isXTurn;
}
// Private helper method for makeMove.
// Given a row and col, as well as player index,
// places an X or an O in that row and col.
// Throws an IllegalArgumentException if the position is
// invalid, whether that be out of bounds or already occupied.
// Board bounds are [0, 2] for both rows and cols.
private void makeMove(int row, int col, char player) {
if (row < 0 || row >= board.length ||
col < 0 || col >= board[0].length) {
throw new IllegalArgumentException("Invalid board position: " + row + "," + col);
}
if (board[row][col] != '-') {
throw new IllegalArgumentException("Space already occupied: " + row + "," + col);
}
board[row][col] = player;
}
// Returns a String containing instructions to play the game.
public String instructions() {
String result = "";
result += "Player 1 is X and goes first. Choose where to play by entering a row and\n";
result += "column number, where (0, 0) is the upper left and (2, 2) is the lower right.\n";
result += "Spaces show as a - are empty. The game ends when one player marks three spaces\n";
result += "in a row, in which case that player wins, or when the board is full, in which\n";
result += "case the game ends in a tie.";
return result;
}
// Returns a String representation of the current state of the board.
public String toString() {
String result = "";
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
result += board[i][j] + " ";
}
result += "\n";
}
return result;
}
}

View File

@@ -0,0 +1,83 @@
import java.awt.*;
public class Client {
public static void main(String[] args) {
// 1. Create a new picture sized 400 x 400 pixels
// 2. Call divide divide canvas
// 3. Save the image to display it
Picture pic = new Picture(500,500);
divideCanvas(pic, 4);
pic.save("boner.jpg");
}
public static void divideCanvas(Picture p, int n) {
Color[][] pixels = p.getPixels();
divideCanvas(p, pixels, n, new Point(0, 0), new Point(p.width() - 1, p.height() - 1));
p.setPixels(pixels);
}
private static void divideCanvas(Picture p, Color[][] pixels, int n, Point one, Point two) {
// extract coordinates from points
int x1 = one.x;
int x2 = two.x;
int y1 = one.y;
int y2 = two.y;
// make useful boundary points
Point topLeft = new Point(x1, y1);
Point topMiddle = new Point((x1 + x2) / 2, y1);
// Point topRight = new Point(x2,y1);
Point middleLeft = new Point(x1, (y1 + y2) / 2);
Point middleMiddle = new Point(topMiddle.x, middleLeft.y);
Point middleRight = new Point(x2, middleLeft.y);
// Point bottomLeft = new Point(x1,y2);
Point bottomMiddle = new Point(topMiddle.x, y2);
Point bottomRight = new Point(x2, y2);
// if we are actually dividing, do this
if (n != 0) {
// System.out.println(topLeft);
// System.out.println(topMiddle);
// System.out.println(middleLeft);
// System.out.println(middleMiddle);
// System.out.println(middleRight);
// System.out.println(bottomMiddle);
// System.out.println(bottomRight);
fill(pixels, topLeft.x, middleMiddle.x, topLeft.y, middleMiddle.y);
fill(pixels, topMiddle.x, middleRight.x, topMiddle.y, middleRight.y);
fill(pixels, middleLeft.x, bottomMiddle.x, middleLeft.y, bottomMiddle.y);
fill(pixels, middleMiddle.x, bottomRight.x, middleMiddle.y, bottomRight.y);
divideCanvas(p, pixels, n - 1, topLeft, middleMiddle);
divideCanvas(p, pixels, n - 1, topMiddle, middleRight);
divideCanvas(p, pixels, n - 1, middleLeft, bottomMiddle);
divideCanvas(p, pixels, n - 1, middleMiddle, bottomRight);
}
// otherwise, just draw the border
else {
fill(pixels,topLeft.x,bottomRight.x,topLeft.y,bottomRight.y);
}
}
public static void fill(Color[][] pixels, int x1, int x2, int y1, int y2) {
for (int i = y1 + 1; i <= y2 - 1; i++) {
for (int n = x1 + 1; n <= x2-1; n++) {
pixels[i][n] = Color.WHITE;
}
}
// outer borders
for (int i = y1; i <= y2; i++) {
pixels[i][x1] = Color.BLACK;
pixels[i][x2] = Color.BLACK;
}
for (int i = x1; i <= x2; i++) {
pixels[y1][i] = Color.BLACK;
pixels[y2][i] = Color.BLACK;
}
}
}

View File

@@ -0,0 +1,505 @@
/******************************************************************************
* Compilation: javac Picture.java
* Execution: java Picture imagename
* Dependencies: none
*
* Data type for manipulating individual pixels of an image. The original
* image can be read from a file in JPG, GIF, or PNG format, or the
* user can create a blank image of a given dimension. Includes methods for
* displaying the image in a window on the screen or saving to a file.
*
* % java Picture mandrill.jpg
*
* Remarks
* -------
* - pixel (x, y) is column x and row y, where (0, 0) is upper left
*
******************************************************************************/
import java.awt.Color;
import java.awt.FileDialog;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/**
* This class provides methods for manipulating individual pixels of
* an image using the RGB color format. The alpha component (for transparency)
* is not currently supported.
* The original image can be read from a {@code PNG}, {@code GIF},
* or {@code JPEG} file or the user can create a blank image of a given dimension.
* This class includes methods for displaying the image in a window on
* the screen or saving it to a file.
* <p>
* Pixel (<em>col</em>, <em>row</em>) is column <em>col</em> and row <em>row</em>.
* By default, the origin (0, 0) is the pixel in the top-left corner,
* which is a common convention in image processing.
* The method {@link #setOriginLowerLeft()} change the origin to the lower left.
* <p>
* The {@code get()} and {@code set()} methods use {@link Color} objects to get
* or set the color of the specified pixel.
* The {@code getRGB()} and {@code setRGB()} methods use a 32-bit {@code int}
* to encode the color, thereby avoiding the need to create temporary
* {@code Color} objects. The red (R), green (G), and blue (B) components
* are encoded using the least significant 24 bits.
* Given a 32-bit {@code int} encoding the color, the following code extracts
* the RGB components:
* <blockquote><pre>
* int r = (rgb &gt;&gt; 16) &amp; 0xFF;
* int g = (rgb &gt;&gt; 8) &amp; 0xFF;
* int b = (rgb &gt;&gt; 0) &amp; 0xFF;
* </pre></blockquote>
* Given the RGB components (8-bits each) of a color,
* the following statement packs it into a 32-bit {@code int}:
* <blockquote><pre>
* int rgb = (r &lt;&lt; 16) + (g &lt;&lt; 8) + (b &lt;&lt; 0);
* </pre></blockquote>
* <p>
* A <em>W</em>-by-<em>H</em> picture uses ~ 4 <em>W H</em> bytes of memory,
* since the color of each pixel is encoded as a 32-bit <code>int</code>.
* <p>
* For additional documentation, see
* <a href="https://introcs.cs.princeton.edu/31datatype">Section 3.1</a> of
* <i>Computer Science: An Interdisciplinary Approach</i>
* by Robert Sedgewick and Kevin Wayne.
* See {@link GrayscalePicture} for a version that supports grayscale images.
*
* @author Robert Sedgewick
* @author Kevin Wayne
*/
public final class Picture implements ActionListener {
private BufferedImage image; // the rasterized image
private JFrame frame; // on-screen view
private String filename; // name of file
private boolean isOriginUpperLeft = true; // location of origin
private final int width, height; // width and height
/**
* Creates a {@code width}-by-{@code height} picture, with {@code width} columns
* and {@code height} rows, where each pixel is black.
*
* @param width the width of the picture
* @param height the height of the picture
* @throws IllegalArgumentException if {@code width} is negative or zero
* @throws IllegalArgumentException if {@code height} is negative or zero
*/
public Picture(int width, int height) {
if (width <= 0) throw new IllegalArgumentException("width must be positive");
if (height <= 0) throw new IllegalArgumentException("height must be positive");
this.width = width;
this.height = height;
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// set to TYPE_INT_ARGB here and in next constructor to support transparency
}
/**
* Creates a new picture that is a deep copy of the argument picture.
*
* @param picture the picture to copy
* @throws IllegalArgumentException if {@code picture} is {@code null}
*/
public Picture(Picture picture) {
if (picture == null) throw new IllegalArgumentException("constructor argument is null");
width = picture.width();
height = picture.height();
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
filename = picture.filename;
isOriginUpperLeft = picture.isOriginUpperLeft;
for (int col = 0; col < width(); col++)
for (int row = 0; row < height(); row++)
image.setRGB(col, row, picture.image.getRGB(col, row));
}
/**
* Creates a picture by reading an image from a file or URL.
*
* @param name the name of the file (.png, .gif, or .jpg) or URL.
* @throws IllegalArgumentException if cannot read image
* @throws IllegalArgumentException if {@code name} is {@code null}
*/
public Picture(String name) {
if (name == null) throw new IllegalArgumentException("constructor argument is null");
this.filename = name;
try {
// try to read from file in working directory
File file = new File(name);
if (file.isFile()) {
image = ImageIO.read(file);
}
else {
// resource relative to .class file
URL url = getClass().getResource(filename);
// resource relative to classloader root
if (url == null) {
url = getClass().getClassLoader().getResource(name);
}
// or URL from web
if (url == null) {
url = new URL(name);
}
image = ImageIO.read(url);
}
if (image == null) {
throw new IllegalArgumentException("could not read image: " + name);
}
width = image.getWidth(null);
height = image.getHeight(null);
}
catch (IOException ioe) {
throw new IllegalArgumentException("could not open image: " + name, ioe);
}
}
/**
* Creates a picture by reading the image from a PNG, GIF, or JPEG file.
*
* @param file the file
* @throws IllegalArgumentException if cannot read image
* @throws IllegalArgumentException if {@code file} is {@code null}
*/
public Picture(File file) {
if (file == null) throw new IllegalArgumentException("constructor argument is null");
try {
image = ImageIO.read(file);
}
catch (IOException ioe) {
throw new IllegalArgumentException("could not open file: " + file, ioe);
}
if (image == null) {
throw new IllegalArgumentException("could not read file: " + file);
}
width = image.getWidth(null);
height = image.getHeight(null);
filename = file.getName();
}
/**
* Returns a {@link JLabel} containing this picture, for embedding in a {@link JPanel},
* {@link JFrame} or other GUI widget.
*
* @return the {@code JLabel}
*/
public JLabel getJLabel() {
if (image == null) return null; // no image available
ImageIcon icon = new ImageIcon(image);
return new JLabel(icon);
}
/**
* Sets the origin to be the upper left pixel. This is the default.
*/
public void setOriginUpperLeft() {
isOriginUpperLeft = true;
}
/**
* Sets the origin to be the lower left pixel.
*/
public void setOriginLowerLeft() {
isOriginUpperLeft = false;
}
/**
* Displays the picture in a window on the screen.
*/
public void show() {
// create the GUI for viewing the image if needed
if (frame == null) {
frame = new JFrame();
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem menuItem1 = new JMenuItem(" Save... ");
menuItem1.addActionListener(this);
// use getMenuShortcutKeyMaskEx() in Java 10 (getMenuShortcutKeyMask() deprecated)
menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
menu.add(menuItem1);
frame.setJMenuBar(menuBar);
frame.setContentPane(getJLabel());
// f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
if (filename == null) frame.setTitle(width + "-by-" + height);
else frame.setTitle(filename);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
}
// draw
frame.repaint();
}
/**
* Returns the height of the picture.
*
* @return the height of the picture (in pixels)
*/
public int height() {
return height;
}
/**
* Returns the width of the picture.
*
* @return the width of the picture (in pixels)
*/
public int width() {
return width;
}
private void validateRowIndex(int row) {
if (row < 0 || row >= height())
throw new IllegalArgumentException("row index must be between 0 and " + (height() - 1) + ": " + row);
}
private void validateColumnIndex(int col) {
if (col < 0 || col >= width())
throw new IllegalArgumentException("column index must be between 0 and " + (width() - 1) + ": " + col);
}
/**
* Returns the color of pixel ({@code col}, {@code row}) as a {@link java.awt.Color}.
*
* @param col the column index
* @param row the row index
* @return the color of pixel ({@code col}, {@code row})
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
*/
public Color get(int col, int row) {
validateColumnIndex(col);
validateRowIndex(row);
int rgb = getRGB(col, row);
return new Color(rgb);
}
/**
* Returns the color of pixel ({@code col}, {@code row}) as an {@code int}.
* Using this method can be more efficient than {@link #get(int, int)} because
* it does not create a {@code Color} object.
*
* @param col the column index
* @param row the row index
* @return the integer representation of the color of pixel ({@code col}, {@code row})
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
*/
public int getRGB(int col, int row) {
validateColumnIndex(col);
validateRowIndex(row);
if (isOriginUpperLeft) return image.getRGB(col, row);
else return image.getRGB(col, height - row - 1);
}
/**
* Sets the color of pixel ({@code col}, {@code row}) to given color.
*
* @param col the column index
* @param row the row index
* @param color the color
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
* @throws IllegalArgumentException if {@code color} is {@code null}
*/
public void set(int col, int row, Color color) {
validateColumnIndex(col);
validateRowIndex(row);
if (color == null) throw new IllegalArgumentException("color argument is null");
int rgb = color.getRGB();
setRGB(col, row, rgb);
}
/**
* Sets the color of pixel ({@code col}, {@code row}) to given color.
*
* @param col the column index
* @param row the row index
* @param rgb the integer representation of the color
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
*/
public void setRGB(int col, int row, int rgb) {
validateColumnIndex(col);
validateRowIndex(row);
if (isOriginUpperLeft) image.setRGB(col, row, rgb);
else image.setRGB(col, height - row - 1, rgb);
}
/**
* Returns true if this picture is equal to the argument picture.
*
* @param other the other picture
* @return {@code true} if this picture is the same dimension as {@code other}
* and if all pixels have the same color; {@code false} otherwise
*/
public boolean equals(Object other) {
if (other == this) return true;
if (other == null) return false;
if (other.getClass() != this.getClass()) return false;
Picture that = (Picture) other;
if (this.width() != that.width()) return false;
if (this.height() != that.height()) return false;
for (int col = 0; col < width(); col++)
for (int row = 0; row < height(); row++)
if (this.getRGB(col, row) != that.getRGB(col, row)) return false;
return true;
}
/**
* Returns a string representation of this picture.
* The result is a <code>width</code>-by-<code>height</code> matrix of pixels,
* where the color of a pixel is represented using 6 hex digits to encode
* the red, green, and blue components.
*
* @return a string representation of this picture
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(width +"-by-" + height + " picture (RGB values given in hex)\n");
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int rgb = 0;
if (isOriginUpperLeft) rgb = image.getRGB(col, row);
else rgb = image.getRGB(col, height - row - 1);
sb.append(String.format("#%06X ", rgb & 0xFFFFFF));
}
sb.append("\n");
}
return sb.toString().trim();
}
/**
* This operation is not supported because pictures are mutable.
*
* @return does not return a value
* @throws UnsupportedOperationException if called
*/
public int hashCode() {
throw new UnsupportedOperationException("hashCode() is not supported because pictures are mutable");
}
/**
* Saves the picture to a file in either PNG or JPEG format.
* The filetype extension must be either .png or .jpg.
*
* @param name the name of the file
* @throws IllegalArgumentException if {@code name} is {@code null}
*/
public void save(String name) {
if (name == null) throw new IllegalArgumentException("argument to save() is null");
save(new File(name));
filename = name;
}
/**
* Saves the picture to a file in a PNG or JPEG image format.
*
* @param file the file
* @throws IllegalArgumentException if {@code file} is {@code null}
*/
public void save(File file) {
if (file == null) throw new IllegalArgumentException("argument to save() is null");
filename = file.getName();
if (frame != null) frame.setTitle(filename);
String suffix = filename.substring(filename.lastIndexOf('.') + 1);
if ("jpg".equalsIgnoreCase(suffix) || "png".equalsIgnoreCase(suffix)) {
try {
ImageIO.write(image, suffix, file);
}
catch (IOException e) {
e.printStackTrace();
}
}
else {
System.out.println("Error: filename must end in .jpg or .png");
}
}
/**
* Opens a save dialog box when the user selects "Save As" from the menu.
*/
@Override
public void actionPerformed(ActionEvent e) {
FileDialog chooser = new FileDialog(frame,
"Use a .png or .jpg extension", FileDialog.SAVE);
chooser.setVisible(true);
if (chooser.getFile() != null) {
save(chooser.getDirectory() + File.separator + chooser.getFile());
}
}
/**
* Unit tests this {@code Picture} data type.
* Reads a picture specified by the command-line argument,
* and shows it in a window on the screen.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
Picture picture = new Picture(args[0]);
System.out.printf("%d-by-%d\n", picture.width(), picture.height());
picture.show();
}
/**
* Returns the pixels in the picture as a two-dimensional array.
*
* @return a two-dimensional array of the colors of the pixels in the picture.
*/
public Color[][] getPixels() {
Color[][] pixels = new Color[height][width];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
pixels[j][i] = get(i, j);
}
}
return pixels;
}
/**
* Sets all pixels in the picture based on the given two-dimensional array.
*
* @param pixels the new colors of the pixels for the picture
* @throws IllegalArgumentException if the dimensions of {@code pixels} do not match
* the dimensions of the picture
*/
public void setPixels(Color[][] pixels) {
if (pixels.length != height || pixels[0].length != width) {
throw new IllegalArgumentException("Wrong dimensions of pixel array");
}
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
set(i, j, pixels[j][i]);
}
}
}
}

View File

@@ -0,0 +1,10 @@
public class Point {
public int x;
public int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

27
c2/fill/Client.java Normal file
View File

@@ -0,0 +1,27 @@
import java.awt.Color;
public class Client {
public static void main(String[] args) {
Picture pic = new Picture(100, 100);
Color[][] pixels = pic.getPixels();
// 1. Create a new picture with size 400 x 400
// 2. Get the pixels out of the image
// 3. Call fill, providing a specific region
fill(pixels,0,40,0,40);
// 4. Set the pixels of the image
// 5. Save the image to display it
pic.setPixels(pixels);
pic.save("white.jpg");
}
// TODO: Implement fill below (this solution can be iterative)
public static void fill(Color[][] pixels, int x1, int x2, int y1, int y2) {
for (int i = y1 + 1; i < y2 - 1; i++) {
for (int n = x1 + 1; n < x2 - 1; n++) {
pixels[i][n] = new Color(255,255,255);
}
}
}
}

505
c2/fill/Picture.java Normal file
View File

@@ -0,0 +1,505 @@
/******************************************************************************
* Compilation: javac Picture.java
* Execution: java Picture imagename
* Dependencies: none
*
* Data type for manipulating individual pixels of an image. The original
* image can be read from a file in JPG, GIF, or PNG format, or the
* user can create a blank image of a given dimension. Includes methods for
* displaying the image in a window on the screen or saving to a file.
*
* % java Picture mandrill.jpg
*
* Remarks
* -------
* - pixel (x, y) is column x and row y, where (0, 0) is upper left
*
******************************************************************************/
import java.awt.Color;
import java.awt.FileDialog;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/**
* This class provides methods for manipulating individual pixels of
* an image using the RGB color format. The alpha component (for transparency)
* is not currently supported.
* The original image can be read from a {@code PNG}, {@code GIF},
* or {@code JPEG} file or the user can create a blank image of a given dimension.
* This class includes methods for displaying the image in a window on
* the screen or saving it to a file.
* <p>
* Pixel (<em>col</em>, <em>row</em>) is column <em>col</em> and row <em>row</em>.
* By default, the origin (0, 0) is the pixel in the top-left corner,
* which is a common convention in image processing.
* The method {@link #setOriginLowerLeft()} change the origin to the lower left.
* <p>
* The {@code get()} and {@code set()} methods use {@link Color} objects to get
* or set the color of the specified pixel.
* The {@code getRGB()} and {@code setRGB()} methods use a 32-bit {@code int}
* to encode the color, thereby avoiding the need to create temporary
* {@code Color} objects. The red (R), green (G), and blue (B) components
* are encoded using the least significant 24 bits.
* Given a 32-bit {@code int} encoding the color, the following code extracts
* the RGB components:
* <blockquote><pre>
* int r = (rgb &gt;&gt; 16) &amp; 0xFF;
* int g = (rgb &gt;&gt; 8) &amp; 0xFF;
* int b = (rgb &gt;&gt; 0) &amp; 0xFF;
* </pre></blockquote>
* Given the RGB components (8-bits each) of a color,
* the following statement packs it into a 32-bit {@code int}:
* <blockquote><pre>
* int rgb = (r &lt;&lt; 16) + (g &lt;&lt; 8) + (b &lt;&lt; 0);
* </pre></blockquote>
* <p>
* A <em>W</em>-by-<em>H</em> picture uses ~ 4 <em>W H</em> bytes of memory,
* since the color of each pixel is encoded as a 32-bit <code>int</code>.
* <p>
* For additional documentation, see
* <a href="https://introcs.cs.princeton.edu/31datatype">Section 3.1</a> of
* <i>Computer Science: An Interdisciplinary Approach</i>
* by Robert Sedgewick and Kevin Wayne.
* See {@link GrayscalePicture} for a version that supports grayscale images.
*
* @author Robert Sedgewick
* @author Kevin Wayne
*/
public final class Picture implements ActionListener {
private BufferedImage image; // the rasterized image
private JFrame frame; // on-screen view
private String filename; // name of file
private boolean isOriginUpperLeft = true; // location of origin
private final int width, height; // width and height
/**
* Creates a {@code width}-by-{@code height} picture, with {@code width} columns
* and {@code height} rows, where each pixel is black.
*
* @param width the width of the picture
* @param height the height of the picture
* @throws IllegalArgumentException if {@code width} is negative or zero
* @throws IllegalArgumentException if {@code height} is negative or zero
*/
public Picture(int width, int height) {
if (width <= 0) throw new IllegalArgumentException("width must be positive");
if (height <= 0) throw new IllegalArgumentException("height must be positive");
this.width = width;
this.height = height;
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// set to TYPE_INT_ARGB here and in next constructor to support transparency
}
/**
* Creates a new picture that is a deep copy of the argument picture.
*
* @param picture the picture to copy
* @throws IllegalArgumentException if {@code picture} is {@code null}
*/
public Picture(Picture picture) {
if (picture == null) throw new IllegalArgumentException("constructor argument is null");
width = picture.width();
height = picture.height();
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
filename = picture.filename;
isOriginUpperLeft = picture.isOriginUpperLeft;
for (int col = 0; col < width(); col++)
for (int row = 0; row < height(); row++)
image.setRGB(col, row, picture.image.getRGB(col, row));
}
/**
* Creates a picture by reading an image from a file or URL.
*
* @param name the name of the file (.png, .gif, or .jpg) or URL.
* @throws IllegalArgumentException if cannot read image
* @throws IllegalArgumentException if {@code name} is {@code null}
*/
public Picture(String name) {
if (name == null) throw new IllegalArgumentException("constructor argument is null");
this.filename = name;
try {
// try to read from file in working directory
File file = new File(name);
if (file.isFile()) {
image = ImageIO.read(file);
}
else {
// resource relative to .class file
URL url = getClass().getResource(filename);
// resource relative to classloader root
if (url == null) {
url = getClass().getClassLoader().getResource(name);
}
// or URL from web
if (url == null) {
url = new URL(name);
}
image = ImageIO.read(url);
}
if (image == null) {
throw new IllegalArgumentException("could not read image: " + name);
}
width = image.getWidth(null);
height = image.getHeight(null);
}
catch (IOException ioe) {
throw new IllegalArgumentException("could not open image: " + name, ioe);
}
}
/**
* Creates a picture by reading the image from a PNG, GIF, or JPEG file.
*
* @param file the file
* @throws IllegalArgumentException if cannot read image
* @throws IllegalArgumentException if {@code file} is {@code null}
*/
public Picture(File file) {
if (file == null) throw new IllegalArgumentException("constructor argument is null");
try {
image = ImageIO.read(file);
}
catch (IOException ioe) {
throw new IllegalArgumentException("could not open file: " + file, ioe);
}
if (image == null) {
throw new IllegalArgumentException("could not read file: " + file);
}
width = image.getWidth(null);
height = image.getHeight(null);
filename = file.getName();
}
/**
* Returns a {@link JLabel} containing this picture, for embedding in a {@link JPanel},
* {@link JFrame} or other GUI widget.
*
* @return the {@code JLabel}
*/
public JLabel getJLabel() {
if (image == null) return null; // no image available
ImageIcon icon = new ImageIcon(image);
return new JLabel(icon);
}
/**
* Sets the origin to be the upper left pixel. This is the default.
*/
public void setOriginUpperLeft() {
isOriginUpperLeft = true;
}
/**
* Sets the origin to be the lower left pixel.
*/
public void setOriginLowerLeft() {
isOriginUpperLeft = false;
}
/**
* Displays the picture in a window on the screen.
*/
public void show() {
// create the GUI for viewing the image if needed
if (frame == null) {
frame = new JFrame();
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem menuItem1 = new JMenuItem(" Save... ");
menuItem1.addActionListener(this);
// use getMenuShortcutKeyMaskEx() in Java 10 (getMenuShortcutKeyMask() deprecated)
menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
menu.add(menuItem1);
frame.setJMenuBar(menuBar);
frame.setContentPane(getJLabel());
// f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
if (filename == null) frame.setTitle(width + "-by-" + height);
else frame.setTitle(filename);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
}
// draw
frame.repaint();
}
/**
* Returns the height of the picture.
*
* @return the height of the picture (in pixels)
*/
public int height() {
return height;
}
/**
* Returns the width of the picture.
*
* @return the width of the picture (in pixels)
*/
public int width() {
return width;
}
private void validateRowIndex(int row) {
if (row < 0 || row >= height())
throw new IllegalArgumentException("row index must be between 0 and " + (height() - 1) + ": " + row);
}
private void validateColumnIndex(int col) {
if (col < 0 || col >= width())
throw new IllegalArgumentException("column index must be between 0 and " + (width() - 1) + ": " + col);
}
/**
* Returns the color of pixel ({@code col}, {@code row}) as a {@link java.awt.Color}.
*
* @param col the column index
* @param row the row index
* @return the color of pixel ({@code col}, {@code row})
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
*/
public Color get(int col, int row) {
validateColumnIndex(col);
validateRowIndex(row);
int rgb = getRGB(col, row);
return new Color(rgb);
}
/**
* Returns the color of pixel ({@code col}, {@code row}) as an {@code int}.
* Using this method can be more efficient than {@link #get(int, int)} because
* it does not create a {@code Color} object.
*
* @param col the column index
* @param row the row index
* @return the integer representation of the color of pixel ({@code col}, {@code row})
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
*/
public int getRGB(int col, int row) {
validateColumnIndex(col);
validateRowIndex(row);
if (isOriginUpperLeft) return image.getRGB(col, row);
else return image.getRGB(col, height - row - 1);
}
/**
* Sets the color of pixel ({@code col}, {@code row}) to given color.
*
* @param col the column index
* @param row the row index
* @param color the color
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
* @throws IllegalArgumentException if {@code color} is {@code null}
*/
public void set(int col, int row, Color color) {
validateColumnIndex(col);
validateRowIndex(row);
if (color == null) throw new IllegalArgumentException("color argument is null");
int rgb = color.getRGB();
setRGB(col, row, rgb);
}
/**
* Sets the color of pixel ({@code col}, {@code row}) to given color.
*
* @param col the column index
* @param row the row index
* @param rgb the integer representation of the color
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
*/
public void setRGB(int col, int row, int rgb) {
validateColumnIndex(col);
validateRowIndex(row);
if (isOriginUpperLeft) image.setRGB(col, row, rgb);
else image.setRGB(col, height - row - 1, rgb);
}
/**
* Returns true if this picture is equal to the argument picture.
*
* @param other the other picture
* @return {@code true} if this picture is the same dimension as {@code other}
* and if all pixels have the same color; {@code false} otherwise
*/
public boolean equals(Object other) {
if (other == this) return true;
if (other == null) return false;
if (other.getClass() != this.getClass()) return false;
Picture that = (Picture) other;
if (this.width() != that.width()) return false;
if (this.height() != that.height()) return false;
for (int col = 0; col < width(); col++)
for (int row = 0; row < height(); row++)
if (this.getRGB(col, row) != that.getRGB(col, row)) return false;
return true;
}
/**
* Returns a string representation of this picture.
* The result is a <code>width</code>-by-<code>height</code> matrix of pixels,
* where the color of a pixel is represented using 6 hex digits to encode
* the red, green, and blue components.
*
* @return a string representation of this picture
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(width +"-by-" + height + " picture (RGB values given in hex)\n");
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int rgb = 0;
if (isOriginUpperLeft) rgb = image.getRGB(col, row);
else rgb = image.getRGB(col, height - row - 1);
sb.append(String.format("#%06X ", rgb & 0xFFFFFF));
}
sb.append("\n");
}
return sb.toString().trim();
}
/**
* This operation is not supported because pictures are mutable.
*
* @return does not return a value
* @throws UnsupportedOperationException if called
*/
public int hashCode() {
throw new UnsupportedOperationException("hashCode() is not supported because pictures are mutable");
}
/**
* Saves the picture to a file in either PNG or JPEG format.
* The filetype extension must be either .png or .jpg.
*
* @param name the name of the file
* @throws IllegalArgumentException if {@code name} is {@code null}
*/
public void save(String name) {
if (name == null) throw new IllegalArgumentException("argument to save() is null");
save(new File(name));
filename = name;
}
/**
* Saves the picture to a file in a PNG or JPEG image format.
*
* @param file the file
* @throws IllegalArgumentException if {@code file} is {@code null}
*/
public void save(File file) {
if (file == null) throw new IllegalArgumentException("argument to save() is null");
filename = file.getName();
if (frame != null) frame.setTitle(filename);
String suffix = filename.substring(filename.lastIndexOf('.') + 1);
if ("jpg".equalsIgnoreCase(suffix) || "png".equalsIgnoreCase(suffix)) {
try {
ImageIO.write(image, suffix, file);
}
catch (IOException e) {
e.printStackTrace();
}
}
else {
System.out.println("Error: filename must end in .jpg or .png");
}
}
/**
* Opens a save dialog box when the user selects "Save As" from the menu.
*/
@Override
public void actionPerformed(ActionEvent e) {
FileDialog chooser = new FileDialog(frame,
"Use a .png or .jpg extension", FileDialog.SAVE);
chooser.setVisible(true);
if (chooser.getFile() != null) {
save(chooser.getDirectory() + File.separator + chooser.getFile());
}
}
/**
* Unit tests this {@code Picture} data type.
* Reads a picture specified by the command-line argument,
* and shows it in a window on the screen.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
Picture picture = new Picture(args[0]);
System.out.printf("%d-by-%d\n", picture.width(), picture.height());
picture.show();
}
/**
* Returns the pixels in the picture as a two-dimensional array.
*
* @return a two-dimensional array of the colors of the pixels in the picture.
*/
public Color[][] getPixels() {
Color[][] pixels = new Color[height][width];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
pixels[j][i] = get(i, j);
}
}
return pixels;
}
/**
* Sets all pixels in the picture based on the given two-dimensional array.
*
* @param pixels the new colors of the pixels for the picture
* @throws IllegalArgumentException if the dimensions of {@code pixels} do not match
* the dimensions of the picture
*/
public void setPixels(Color[][] pixels) {
if (pixels.length != height || pixels[0].length != width) {
throw new IllegalArgumentException("Wrong dimensions of pixel array");
}
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
set(i, j, pixels[j][i]);
}
}
}
}

BIN
c2/fill/white.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

35
c2/mondrian/Client.java Normal file
View File

@@ -0,0 +1,35 @@
import java.awt.*;
import java.util.*;
public class Client {
public static void main(String[] args) throws Exception {
Scanner console = new Scanner(System.in);
System.out.println("Welcome to the CSE 123 Mondrian Art Generator!");
int choice = 0;
while (choice != 1 && choice != 2) {
System.out.print("Enter 1 for a basic Mondrian or 2 for a complex Mondrian: ");
choice = console.nextInt();
}
System.out.print("Enter image width (>= 300px): ");
int width = console.nextInt();
System.out.print("Enter image height (>= 300px): ");
int height = console.nextInt();
Mondrian mond = new Mondrian();
Picture pic = new Picture(width, height);
Color[][] pixels = pic.getPixels();
if (choice == 1) {
mond.paintBasicMondrian(pixels);
} else { // choice == 2
mond.paintComplexMondrian(pixels);
}
pic.setPixels(pixels);
pic.save(choice == 1 ? "basic.png" : "extension.png");
pic.show();
System.out.println("Enjoy your artwork!");
}
}

97
c2/mondrian/Mondrian.java Normal file
View File

@@ -0,0 +1,97 @@
import java.awt.*;
import java.util.Random;
public class Mondrian {
private int canvasWidth;
private int canvasHeight;
private static final int MIN_REGION_SIZE = 10;
private Random rand;
public void paintBasicMondrian(Color[][] pixels) {
rand = new Random();
int width = pixels[0].length;
int height = pixels.length;
this.canvasWidth = width;
this.canvasHeight = height;
divideCanvas(pixels, 0, width, 0, height);
}
public void paintComplexMondrian(Color[][] pixels) {
}
private void divideCanvas(Color[][] pixels, int x1, int x2, int y1, int y2) {
if (x2 - x1 >= MIN_REGION_SIZE * 3 && y2 - y1 >= MIN_REGION_SIZE * 3) {
//int hSplit = (x2 - x1) / 2 + x1;
//int vSplit = (y2 - y1) / 2 + y1;
int hSplit = rand.nextInt(x1+1, x2-10);
int vSplit = rand.nextInt(y1+1, y2-10);
//if (x2 - x1 >= canvasWidth / 4) {
//int rangeX = (x2 - x1 - MIN_REGION_SIZE * 2) / MIN_REGION_SIZE;
// hSplit = rand.nextInt(rangeX) * MIN_REGION_SIZE + x1 + MIN_REGION_SIZE;
// hSplit = Math.min(hSplit, x2 - MIN_REGION_SIZE); // Ensure hSplit is within canvas bounds
// hSplit = Math.max(hSplit, x1 + MIN_REGION_SIZE); // Ensure hSplit is at least MIN_REGION_SIZE away from the canvas edge
//}
//if (y2 - y1 >= canvasHeight / 4) {
// int rangeY = (y2 - y1 - MIN_REGION_SIZE * 2) / MIN_REGION_SIZE;
// vSplit = rand.nextInt(rangeY) * MIN_REGION_SIZE + y1 + MIN_REGION_SIZE;
// vSplit = Math.min(vSplit, y2 - MIN_REGION_SIZE); // Ensure vSplit is within canvas bounds
// vSplit = Math.max(vSplit, y1 + MIN_REGION_SIZE); // Ensure vSplit is at least MIN_REGION_SIZE away from the canvas edge
//}
//// Additional constraints for the horizontal splitting points to prevent rectangles wider than canvasWidth/4
//if (x2 - x1 >= canvasWidth / 4) {
// hSplit = Math.min(hSplit, x1 + (x2 - x1) / 2); // Ensure hSplit is not farther than halfway across the canvas
//}
fill(pixels, x1, x2, y1, y2);
if (x2 - x1 >= canvasWidth / 4 && y2 - y1 >= canvasHeight / 4) {
divideCanvas(pixels, x1, hSplit, y1, vSplit);
divideCanvas(pixels, hSplit, x2, y1, vSplit);
divideCanvas(pixels, x1, hSplit, vSplit, y2);
divideCanvas(pixels, hSplit, x2, vSplit, y2);
} else if (x2 - x1 >= canvasWidth / 4) {
divideCanvas(pixels, x1, hSplit, y1, y2);
divideCanvas(pixels, hSplit, x2, y1, y2);
} else if (y2 - y1 >= canvasHeight / 4) {
divideCanvas(pixels, x1, x2, y1, vSplit);
divideCanvas(pixels, x1, x2, vSplit, y2);
}
} else {
fill(pixels, x1, x2, y1, y2);
}
}
private void fill(Color[][] pixels, int x1, int x2, int y1, int y2) {
Color region = getRandomColor();
for (int i = y1; i < y2; i++) {
pixels[i][x1] = Color.BLACK;
pixels[i][x2 - 1] = Color.BLACK;
}
for (int j = x1; j < x2; j++) {
pixels[y1][j] = Color.BLACK;
pixels[y2 - 1][j] = Color.BLACK;
}
for (int i = y1 + 1; i < y2 - 1; i++) {
for (int j = x1 + 1; j < x2 - 1; j++) {
pixels[i][j] = region;
}
}
}
private Color getRandomColor() {
Color[] colors = {Color.RED, Color.YELLOW, Color.CYAN, Color.WHITE};
return colors[rand.nextInt(4)];
}
}

505
c2/mondrian/Picture.java Normal file
View File

@@ -0,0 +1,505 @@
/******************************************************************************
* Compilation: javac Picture.java
* Execution: java Picture imagename
* Dependencies: none
*
* Data type for manipulating individual pixels of an image. The original
* image can be read from a file in JPG, GIF, or PNG format, or the
* user can create a blank image of a given dimension. Includes methods for
* displaying the image in a window on the screen or saving to a file.
*
* % java Picture mandrill.jpg
*
* Remarks
* -------
* - pixel (x, y) is column x and row y, where (0, 0) is upper left
*
******************************************************************************/
import java.awt.Color;
import java.awt.FileDialog;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/**
* This class provides methods for manipulating individual pixels of
* an image using the RGB color format. The alpha component (for transparency)
* is not currently supported.
* The original image can be read from a {@code PNG}, {@code GIF},
* or {@code JPEG} file or the user can create a blank image of a given dimension.
* This class includes methods for displaying the image in a window on
* the screen or saving it to a file.
* <p>
* Pixel (<em>col</em>, <em>row</em>) is column <em>col</em> and row <em>row</em>.
* By default, the origin (0, 0) is the pixel in the top-left corner,
* which is a common convention in image processing.
* The method {@link #setOriginLowerLeft()} change the origin to the lower left.
* <p>
* The {@code get()} and {@code set()} methods use {@link Color} objects to get
* or set the color of the specified pixel.
* The {@code getRGB()} and {@code setRGB()} methods use a 32-bit {@code int}
* to encode the color, thereby avoiding the need to create temporary
* {@code Color} objects. The red (R), green (G), and blue (B) components
* are encoded using the least significant 24 bits.
* Given a 32-bit {@code int} encoding the color, the following code extracts
* the RGB components:
* <blockquote><pre>
* int r = (rgb &gt;&gt; 16) &amp; 0xFF;
* int g = (rgb &gt;&gt; 8) &amp; 0xFF;
* int b = (rgb &gt;&gt; 0) &amp; 0xFF;
* </pre></blockquote>
* Given the RGB components (8-bits each) of a color,
* the following statement packs it into a 32-bit {@code int}:
* <blockquote><pre>
* int rgb = (r &lt;&lt; 16) + (g &lt;&lt; 8) + (b &lt;&lt; 0);
* </pre></blockquote>
* <p>
* A <em>W</em>-by-<em>H</em> picture uses ~ 4 <em>W H</em> bytes of memory,
* since the color of each pixel is encoded as a 32-bit <code>int</code>.
* <p>
* For additional documentation, see
* <a href="https://introcs.cs.princeton.edu/31datatype">Section 3.1</a> of
* <i>Computer Science: An Interdisciplinary Approach</i>
* by Robert Sedgewick and Kevin Wayne.
* See {@link GrayscalePicture} for a version that supports grayscale images.
*
* @author Robert Sedgewick
* @author Kevin Wayne
*/
public final class Picture implements ActionListener {
private BufferedImage image; // the rasterized image
private JFrame frame; // on-screen view
private String filename; // name of file
private boolean isOriginUpperLeft = true; // location of origin
private final int width, height; // width and height
/**
* Creates a {@code width}-by-{@code height} picture, with {@code width} columns
* and {@code height} rows, where each pixel is black.
*
* @param width the width of the picture
* @param height the height of the picture
* @throws IllegalArgumentException if {@code width} is negative or zero
* @throws IllegalArgumentException if {@code height} is negative or zero
*/
public Picture(int width, int height) {
if (width <= 0) throw new IllegalArgumentException("width must be positive");
if (height <= 0) throw new IllegalArgumentException("height must be positive");
this.width = width;
this.height = height;
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// set to TYPE_INT_ARGB here and in next constructor to support transparency
}
/**
* Creates a new picture that is a deep copy of the argument picture.
*
* @param picture the picture to copy
* @throws IllegalArgumentException if {@code picture} is {@code null}
*/
public Picture(Picture picture) {
if (picture == null) throw new IllegalArgumentException("constructor argument is null");
width = picture.width();
height = picture.height();
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
filename = picture.filename;
isOriginUpperLeft = picture.isOriginUpperLeft;
for (int col = 0; col < width(); col++)
for (int row = 0; row < height(); row++)
image.setRGB(col, row, picture.image.getRGB(col, row));
}
/**
* Creates a picture by reading an image from a file or URL.
*
* @param name the name of the file (.png, .gif, or .jpg) or URL.
* @throws IllegalArgumentException if cannot read image
* @throws IllegalArgumentException if {@code name} is {@code null}
*/
public Picture(String name) {
if (name == null) throw new IllegalArgumentException("constructor argument is null");
this.filename = name;
try {
// try to read from file in working directory
File file = new File(name);
if (file.isFile()) {
image = ImageIO.read(file);
}
else {
// resource relative to .class file
URL url = getClass().getResource(filename);
// resource relative to classloader root
if (url == null) {
url = getClass().getClassLoader().getResource(name);
}
// or URL from web
if (url == null) {
url = new URL(name);
}
image = ImageIO.read(url);
}
if (image == null) {
throw new IllegalArgumentException("could not read image: " + name);
}
width = image.getWidth(null);
height = image.getHeight(null);
}
catch (IOException ioe) {
throw new IllegalArgumentException("could not open image: " + name, ioe);
}
}
/**
* Creates a picture by reading the image from a PNG, GIF, or JPEG file.
*
* @param file the file
* @throws IllegalArgumentException if cannot read image
* @throws IllegalArgumentException if {@code file} is {@code null}
*/
public Picture(File file) {
if (file == null) throw new IllegalArgumentException("constructor argument is null");
try {
image = ImageIO.read(file);
}
catch (IOException ioe) {
throw new IllegalArgumentException("could not open file: " + file, ioe);
}
if (image == null) {
throw new IllegalArgumentException("could not read file: " + file);
}
width = image.getWidth(null);
height = image.getHeight(null);
filename = file.getName();
}
/**
* Returns a {@link JLabel} containing this picture, for embedding in a {@link JPanel},
* {@link JFrame} or other GUI widget.
*
* @return the {@code JLabel}
*/
public JLabel getJLabel() {
if (image == null) return null; // no image available
ImageIcon icon = new ImageIcon(image);
return new JLabel(icon);
}
/**
* Sets the origin to be the upper left pixel. This is the default.
*/
public void setOriginUpperLeft() {
isOriginUpperLeft = true;
}
/**
* Sets the origin to be the lower left pixel.
*/
public void setOriginLowerLeft() {
isOriginUpperLeft = false;
}
/**
* Displays the picture in a window on the screen.
*/
public void show() {
// create the GUI for viewing the image if needed
if (frame == null) {
frame = new JFrame();
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem menuItem1 = new JMenuItem(" Save... ");
menuItem1.addActionListener(this);
// use getMenuShortcutKeyMaskEx() in Java 10 (getMenuShortcutKeyMask() deprecated)
menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
menu.add(menuItem1);
frame.setJMenuBar(menuBar);
frame.setContentPane(getJLabel());
// f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
if (filename == null) frame.setTitle(width + "-by-" + height);
else frame.setTitle(filename);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
}
// draw
frame.repaint();
}
/**
* Returns the height of the picture.
*
* @return the height of the picture (in pixels)
*/
public int height() {
return height;
}
/**
* Returns the width of the picture.
*
* @return the width of the picture (in pixels)
*/
public int width() {
return width;
}
private void validateRowIndex(int row) {
if (row < 0 || row >= height())
throw new IllegalArgumentException("row index must be between 0 and " + (height() - 1) + ": " + row);
}
private void validateColumnIndex(int col) {
if (col < 0 || col >= width())
throw new IllegalArgumentException("column index must be between 0 and " + (width() - 1) + ": " + col);
}
/**
* Returns the color of pixel ({@code col}, {@code row}) as a {@link java.awt.Color}.
*
* @param col the column index
* @param row the row index
* @return the color of pixel ({@code col}, {@code row})
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
*/
public Color get(int col, int row) {
validateColumnIndex(col);
validateRowIndex(row);
int rgb = getRGB(col, row);
return new Color(rgb);
}
/**
* Returns the color of pixel ({@code col}, {@code row}) as an {@code int}.
* Using this method can be more efficient than {@link #get(int, int)} because
* it does not create a {@code Color} object.
*
* @param col the column index
* @param row the row index
* @return the integer representation of the color of pixel ({@code col}, {@code row})
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
*/
public int getRGB(int col, int row) {
validateColumnIndex(col);
validateRowIndex(row);
if (isOriginUpperLeft) return image.getRGB(col, row);
else return image.getRGB(col, height - row - 1);
}
/**
* Sets the color of pixel ({@code col}, {@code row}) to given color.
*
* @param col the column index
* @param row the row index
* @param color the color
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
* @throws IllegalArgumentException if {@code color} is {@code null}
*/
public void set(int col, int row, Color color) {
validateColumnIndex(col);
validateRowIndex(row);
if (color == null) throw new IllegalArgumentException("color argument is null");
int rgb = color.getRGB();
setRGB(col, row, rgb);
}
/**
* Sets the color of pixel ({@code col}, {@code row}) to given color.
*
* @param col the column index
* @param row the row index
* @param rgb the integer representation of the color
* @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height}
*/
public void setRGB(int col, int row, int rgb) {
validateColumnIndex(col);
validateRowIndex(row);
if (isOriginUpperLeft) image.setRGB(col, row, rgb);
else image.setRGB(col, height - row - 1, rgb);
}
/**
* Returns true if this picture is equal to the argument picture.
*
* @param other the other picture
* @return {@code true} if this picture is the same dimension as {@code other}
* and if all pixels have the same color; {@code false} otherwise
*/
public boolean equals(Object other) {
if (other == this) return true;
if (other == null) return false;
if (other.getClass() != this.getClass()) return false;
Picture that = (Picture) other;
if (this.width() != that.width()) return false;
if (this.height() != that.height()) return false;
for (int col = 0; col < width(); col++)
for (int row = 0; row < height(); row++)
if (this.getRGB(col, row) != that.getRGB(col, row)) return false;
return true;
}
/**
* Returns a string representation of this picture.
* The result is a <code>width</code>-by-<code>height</code> matrix of pixels,
* where the color of a pixel is represented using 6 hex digits to encode
* the red, green, and blue components.
*
* @return a string representation of this picture
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(width +"-by-" + height + " picture (RGB values given in hex)\n");
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int rgb = 0;
if (isOriginUpperLeft) rgb = image.getRGB(col, row);
else rgb = image.getRGB(col, height - row - 1);
sb.append(String.format("#%06X ", rgb & 0xFFFFFF));
}
sb.append("\n");
}
return sb.toString().trim();
}
/**
* This operation is not supported because pictures are mutable.
*
* @return does not return a value
* @throws UnsupportedOperationException if called
*/
public int hashCode() {
throw new UnsupportedOperationException("hashCode() is not supported because pictures are mutable");
}
/**
* Saves the picture to a file in either PNG or JPEG format.
* The filetype extension must be either .png or .jpg.
*
* @param name the name of the file
* @throws IllegalArgumentException if {@code name} is {@code null}
*/
public void save(String name) {
if (name == null) throw new IllegalArgumentException("argument to save() is null");
save(new File(name));
filename = name;
}
/**
* Saves the picture to a file in a PNG or JPEG image format.
*
* @param file the file
* @throws IllegalArgumentException if {@code file} is {@code null}
*/
public void save(File file) {
if (file == null) throw new IllegalArgumentException("argument to save() is null");
filename = file.getName();
if (frame != null) frame.setTitle(filename);
String suffix = filename.substring(filename.lastIndexOf('.') + 1);
if ("jpg".equalsIgnoreCase(suffix) || "png".equalsIgnoreCase(suffix)) {
try {
ImageIO.write(image, suffix, file);
}
catch (IOException e) {
e.printStackTrace();
}
}
else {
System.out.println("Error: filename must end in .jpg or .png");
}
}
/**
* Opens a save dialog box when the user selects "Save As" from the menu.
*/
@Override
public void actionPerformed(ActionEvent e) {
FileDialog chooser = new FileDialog(frame,
"Use a .png or .jpg extension", FileDialog.SAVE);
chooser.setVisible(true);
if (chooser.getFile() != null) {
save(chooser.getDirectory() + File.separator + chooser.getFile());
}
}
/**
* Unit tests this {@code Picture} data type.
* Reads a picture specified by the command-line argument,
* and shows it in a window on the screen.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
Picture picture = new Picture(args[0]);
System.out.printf("%d-by-%d\n", picture.width(), picture.height());
picture.show();
}
/**
* Returns the pixels in the picture as a two-dimensional array.
*
* @return a two-dimensional array of the colors of the pixels in the picture.
*/
public Color[][] getPixels() {
Color[][] pixels = new Color[height][width];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
pixels[j][i] = get(i, j);
}
}
return pixels;
}
/**
* Sets all pixels in the picture based on the given two-dimensional array.
*
* @param pixels the new colors of the pixels for the picture
* @throws IllegalArgumentException if the dimensions of {@code pixels} do not match
* the dimensions of the picture
*/
public void setPixels(Color[][] pixels) {
if (pixels.length != height || pixels[0].length != width) {
throw new IllegalArgumentException("Wrong dimensions of pixel array");
}
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
set(i, j, pixels[j][i]);
}
}
}
}

BIN
c2/mondrian/basic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

2
c2/mondrian/compile.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
javac *.java && java Client && rm *.class

91
c3/Client.java Normal file
View File

@@ -0,0 +1,91 @@
import java.util.*;
import java.io.*;
public class Client {
public static void main(String[] args) throws FileNotFoundException {
Scanner console = new Scanner(System.in);
System.out.print("Enter quiz file to read: ");
String inFileName = console.nextLine();
File inFile = new File(inFileName);
while (!inFile.exists()) {
System.out.println(" File does not exist. Please try again.");
System.out.print("Enter quiz file to read: ");
inFileName = console.nextLine();
inFile = new File(inFileName);
}
QuizTree quiz = new QuizTree(new Scanner(inFile));
System.out.println("Quiz created!");
System.out.println();
String option = "";
while (!option.equalsIgnoreCase("quit")) {
option = menu(console);
System.out.println();
if (option.equalsIgnoreCase("take")) {
quiz.takeQuiz(console);
System.out.println();
} else if (option.equalsIgnoreCase("creative")) {
// quiz.creativeExtension(); // TODO: Update with any parameters you need!
System.out.println();
} else if (option.equalsIgnoreCase("export")) {
System.out.print("Enter file to export to: ");
String outFileName = console.nextLine();
PrintStream outFile = new PrintStream(new File(outFileName));
quiz.export(outFile);
System.out.println("Quiz exported!");
System.out.println();
} else if (option.equalsIgnoreCase("add")) {
addQ(console, quiz);
System.out.println();
} else if (!option.equalsIgnoreCase("quit")) {
System.out.println(" Invalid choice. Please try again.");
System.out.println();
}
}
}
private static String menu(Scanner console) {
System.out.println("What would you like to do? Choose an option in brackets.");
System.out.println(" [take] quiz");
System.out.println(" [add] question");
System.out.println(" [export] quiz");
System.out.println(" [creative] extension");
System.out.println(" [quit] program");
return console.nextLine();
}
private static void addQ(Scanner console, QuizTree quiz) {
System.out.print("Enter result to replace: ");
String toReplace = console.nextLine();
System.out.print("Enter left choice: ");
String leftChoice = console.nextLine();
System.out.print("Enter right choice: ");
String rightChoice = console.nextLine();
System.out.print("Enter score of choices: ");
String choiceScore = console.nextLine();
System.out.print("Enter left result: ");
String leftResult = console.nextLine();
System.out.print("Enter left score: ");
int leftScore = Integer.parseInt(console.nextLine());
System.out.print("Enter right result: ");
String rightResult = console.nextLine();
System.out.print("Enter right score: ");
int rightScore = Integer.parseInt(console.nextLine());
String choices = leftChoice + "/" + rightChoice + "-" + choiceScore;
leftResult = leftResult + "-" + leftScore;
rightResult = rightResult + "-" + rightScore;
quiz.addQuestion(toReplace, choices, leftResult, rightResult);
}
}

170
c3/QuizTree.java Normal file
View File

@@ -0,0 +1,170 @@
import java.util.*;
import java.io.*;
public class QuizTree {
private QuizTreeNode head;
// PROVIDED
// Returns the given percent rounded to two decimal places.
private double roundTwoPlaces(double percent) {
return (double) Math.round(percent * 100) / 100;
}
public int size() {
return size(head);
}
private int size(QuizTreeNode root) {
if (root == null) {
return 0;
}
return 1 + size(root.left) + size(root.right);
}
public QuizTree(Scanner inputFile) {
this.head = null;
this.head = populateTree(inputFile, head);
}
private void printTree(QuizTreeNode root) {
if (root != null) {
System.out.println(root.data);
printTree(root.left);
printTree(root.right);
}
}
private QuizTreeNode populateTree(Scanner inputFile, QuizTreeNode root) {
if (inputFile.hasNextLine()) {
String next = inputFile.nextLine();
String data;
int score;
// we've reached a result, make a node but dont recurse
if (next.startsWith("END:")) {
data = next.substring(4, next.lastIndexOf("-"));
score = Integer.parseInt(next.substring(next.lastIndexOf("-") + 1));
return new QuizTreeNode(data, score);
}
// otherwise, we're not at the end yet, make a node and keep going
else {
data = next.substring(0, next.lastIndexOf("-"));
score = Integer.parseInt(next.substring(next.lastIndexOf("-") + 1));
root = new QuizTreeNode(data, score);
root.left = populateTree(inputFile, root.left);
root.right = populateTree(inputFile, root.right);
}
}
return root;
}
public void takeQuiz(Scanner console) {
takeQuiz(console, head);
}
private void takeQuiz(Scanner console, QuizTreeNode root) {
if (root.left != null && root.right != null) {
String left = root.data.substring(0, root.data.lastIndexOf("/"));
String right = root.data.substring(root.data.lastIndexOf("/")+1, root.data.length());
System.out.print("Do you prefer " + left + " or " + right + "? ");
String input = console.nextLine();
while (!input.equalsIgnoreCase(left) || !input.equalsIgnoreCase(right)) {
System.out.println(" Invalid response; try again.");
System.out.print("Do you prefer " + left + " or " + right + "? ");
input = console.nextLine();
}
takeQuiz(console, input.equalsIgnoreCase(left) ? root.left : root.right);
}
else {
System.out.println("Your result is: " + root.data);
System.out.println("Your score is: " + root.score);
}
}
public void export(PrintStream outputFile) {
export(outputFile, this.head);
}
private void export(PrintStream outputFile, QuizTreeNode root) {
if (root.left == null && root.right == null) {
outputFile.println("END:" + root.data + "-" + root.score);
} else {
outputFile.println(root.data + "-" + root.score);
}
if (root.left != null) {
export(outputFile, root.left);
}
if (root.right != null) {
export(outputFile, root.right);
}
}
//public void addQuestion(String toReplace, String choices, String leftResult, String rightResult) {
//}
public void addQuestion(String toReplace, String choices, String leftResult, String rightResult) {
head = addQuestion(head, toReplace.toLowerCase(), choices, leftResult, rightResult);
}
private QuizTreeNode addQuestion(QuizTreeNode root, String toReplace, String choices, String leftResult, String rightResult) {
if (root == null) {
return null;
}
if (root.left == null && root.right == null && root.data.equalsIgnoreCase(toReplace)) {
// Parse choices to get left choice, right choice, and score
String choiceText = choices.substring(0, choices.lastIndexOf("-"));
int choiceScore = Integer.parseInt(choices.substring(choices.lastIndexOf("-") + 1));
// Create the new choice node
QuizTreeNode choiceNode = new QuizTreeNode(choiceText, choiceScore);
// Parse leftResult to get left result data and score
String leftData = leftResult.substring(0, leftResult.lastIndexOf("-"));
int leftScore = Integer.parseInt(leftResult.substring(leftResult.lastIndexOf("-") + 1));
QuizTreeNode leftNode = new QuizTreeNode(leftData, leftScore);
// Parse rightResult to get right result data and score
String rightData = rightResult.substring(0, rightResult.lastIndexOf("-"));
int rightScore = Integer.parseInt(rightResult.substring(rightResult.lastIndexOf("-") + 1));
QuizTreeNode rightNode = new QuizTreeNode(rightData, rightScore);
// Set the left and right children of the new choice node
choiceNode.left = leftNode;
choiceNode.right = rightNode;
return choiceNode;
} else {
root.left = addQuestion(root.left, toReplace, choices, leftResult, rightResult);
root.right = addQuestion(root.right, toReplace, choices, leftResult, rightResult);
return root;
}
}
public static class QuizTreeNode {
public final String data;
public final int score;
public QuizTreeNode left;
public QuizTreeNode right;
public QuizTreeNode(String data, int score) {
this(data, score, null, null);
}
public QuizTreeNode(String data, int score, QuizTreeNode left, QuizTreeNode right) {
this.data = data;
this.score = score;
this.left = left;
this.right = right;
}
}
}

9
c3/colors-cereals.txt Normal file
View File

@@ -0,0 +1,9 @@
red/blue-0
yellow/green-1
END:Froot Loops-3
END:Raisin Bran-5
purple/orange-2
END:Frosted Flakes-1
black/white-3
END:Rice Krispies-2
END:Fruity Pebbles-4

3
c3/compile.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
javac *.java && java Client ; rm *.class

Binary file not shown.

49
ciphers/Cipher.java Normal file
View File

@@ -0,0 +1,49 @@
import java.util.*;
import java.io.*;
// Represents a classical cipher that is able to encode a plaintext into a ciphertext, and
// decode a ciphertext into a plaintext. Also capable of encoding and decoding entire files
public abstract class Cipher {
// The minimum character able to be encoded by any cipher
public static final int MIN_CHAR = (int)(' ');
// The maximum character able to be encoded by any cipher
public static final int MAX_CHAR = (int)('}');
// The total number of characters able to be encoded by any cipher (aka. the encodable range)
public static final int TOTAL_CHARS = MAX_CHAR - MIN_CHAR + 1;
// Pre: Throws a FileNotFoundException if a file with the provided 'fileName' doesn't exist
// Post: Applies this Cipher's encryption scheme to the file with the given 'fileName', creating
// a new file to store the results.
public void encryptFile(String fileName) throws FileNotFoundException {
fileHelper(fileName, true, "-encoded");
}
// Pre: Throws a FileNotFoundException if a file with the provided 'fileName' doesn't exist
// Post: Applies this Cipher's decryption scheme (reversing a single round of encryption if applied)
// to the file with the given 'fileName', creating a new file to store the results.
public void decryptFile(String fileName) throws FileNotFoundException {
fileHelper(fileName, false, "-decoded");
}
// Pre: Throws a FileNotFoundException if a file with the provided 'fileName' doesn't exist
// Post: Reads from an input file with 'fileName', either encrypting or decrypting depending on 'encode',
// printing the results to a new file with 'suffix' appended to the input file's name
private void fileHelper(String fileName, boolean encode, String suffix) throws FileNotFoundException{
Scanner sc = new Scanner(new File(fileName));
String out = fileName.split("\\.txt")[0] + suffix + ".txt";
PrintStream ps = new PrintStream(out);
while(sc.hasNextLine()) {
String line = sc.nextLine();
ps.println(encode ? encrypt(line) : decrypt(line));
}
}
// Post: Returns the result of applying this Cipher's encryption scheme to 'input'
public abstract String encrypt(String input);
// Post: Returns the result of applying this Cipher's decryption scheme to 'input'
public abstract String decrypt(String input);
}

45
ciphers/Client.java Normal file
View File

@@ -0,0 +1,45 @@
import java.util.*;
import java.io.*;
public class Client {
// TODO: Change this line once you've implemented a cipher!
public static final Cipher CHOSEN_CIPHER = null;
// (we also encourage you to change Cipher.MIN_CHAR and Cipher.MAX_CHAR when testing!)
public static void main(String[] args) throws FileNotFoundException {
Scanner console = new Scanner(System.in);
System.out.println("Welcome to the CSE 123 cryptography application!");
System.out.println("What would you like to do?");
int chosen = -1;
do {
System.out.println();
System.out.println("(1) Encode / (2) Decode a string");
System.out.println("(3) Encode / (4) Decode a file");
System.out.println("(5) Quit");
System.out.print("Enter your choice here: ");
chosen = Integer.parseInt(console.nextLine());
while (chosen < 1 || chosen > 5) {
System.out.print("Please enter a valid option from above: ");
chosen = Integer.parseInt(console.nextLine());
}
if (chosen == 1 || chosen == 2) {
System.out.println("Please enter the string you'd like to " +
(chosen == 1 ? "encode" : "decode") + ": ");
String input = console.nextLine();
System.out.println(chosen == 1 ? CHOSEN_CIPHER.encrypt(input) :
CHOSEN_CIPHER.decrypt(input));
} else if (chosen == 3 || chosen == 4) {
System.out.print("Please enter the name of the file you'd like to " +
(chosen == 3 ? "encode" : "decode") + ": ");
String fileName = console.nextLine();
if (chosen == 3) {
CHOSEN_CIPHER.encryptFile(fileName);
} else {
CHOSEN_CIPHER.decryptFile(fileName);
}
}
} while (chosen != 5);
}
}

View File

@@ -0,0 +1,11 @@
// TODO: Write your implementation to SubstitutionRandom here!
import java.util.*;
public class SubstitutionRandom extends Substitution {
public static void main(String[] args) {
Random balls = new Random();
int boner = balls.nextInt(4);
System.out.println(boner);
Collections.shuffle()
}
}

19
ciphers/Testing.java Normal file
View File

@@ -0,0 +1,19 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.Assume.assumeTrue;
import java.util.*;
public class Testing {
@Test
@DisplayName("TODO: 1 Your Extension - ' '-'}' Shifter")
public void thirdCaseTest() {
assertTrue(false, "Not yet implemented!");
}
@Test
@DisplayName("TODO: 1 Your Extension - ' '-'}' Shifter")
public void thirdCaseTest() {
assertTrue(false, "Not yet implemented!");
}
}

View File

@@ -0,0 +1 @@
// TODO: Write your implementation to Transposition here!

1
ciphers/Vigenere.java Normal file
View File

@@ -0,0 +1 @@
// TODO: Write your implementation to Vigenere here!

1
ciphers/files/ag.txt Normal file
View File

@@ -0,0 +1 @@
HADBADCABBAGEBAG

4463
ciphers/files/hamlet.txt Normal file

File diff suppressed because it is too large Load Diff

15
ciphers/files/simple.txt Normal file
View File

@@ -0,0 +1,15 @@
Hi everyone,
We're using Ed Discussion for class Q&A.
This is the best place to ask questions about the course, whether curricular or administrative. You will get faster answers here from staff and peers than through email.
Here are some tips:
Search before you post
Heart questions and answers you find useful
Answer questions you feel confident answering
Share interesting course related content with staff and peers
For more information on Ed Discussion, you can refer to the Quick Start Guide.
All the best this semester!
Brett

Binary file not shown.

71
debugging/Book.java Normal file
View File

@@ -0,0 +1,71 @@
import java.util.*;
public class Book implements Media {
private String title;
private String author;
private List<String> authors;
private List<Integer> ratings;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public Book(String title, List<String> authors) {
this.title = title;
this.authors = authors;
}
public String getTitle() {
return this.title;
}
public List<String> getArtists() {
List<String> artists = new ArrayList<>();
if (this.author != null) {
artists.add(this.author);
}
if (this.authors != null) {
for (String author : authors) {
artists.add(author);
}
}
return artists;
}
public void addRating(int score) {
if (this.ratings == null) {
ratings = new ArrayList<>();
}
this.ratings.add(score);
}
public int getNumRatings() {
if (this.ratings == null) {
return 0;
}
return this.ratings.size();
}
public double getAverageRating() {
if (this.ratings == null) {
return 0;
}
int sum = 0;
for (int rating : ratings) {
sum += rating;
}
return (double)sum / this.ratings.size();
}
public String toString() {
return this.title + " by " + this.getArtists() + ": " + this.getAverageRating() +
(this.ratings.size()) + " ratings";
}
}

54
debugging/Debugging.java Normal file
View File

@@ -0,0 +1,54 @@
import java.util.*;
public class Debugging {
public static void main(String[] args) {
Map<String, Set<Integer>> testMap = new TreeMap<>();
Set<Integer> c121 = arrToSet(new int[]{42, 17, 42, 42});
Set<Integer> c122 = arrToSet(new int[]{10, 12, 14});
Set<Integer> c123 = arrToSet(new int[]{100, 99, 98, -97});
testMap.put("cse121", c121);
testMap.put("cse122", c122);
testMap.put("cse123", c123);
Map<String, Set<Integer>> deepCopyMap = deepCopy(testMap);
if (deepCopyMap.isEmpty()) {
System.out.println("{}");
} else {
String line = "";
for (String key : deepCopyMap.keySet()) {
line += key + "=" + deepCopyMap.get(key).toString() + ", ";
}
System.out.println("{" + line.substring(0, line.length() - 2) + "}");
}
}
public static Set<Integer> arrToSet(int[] arr) {
Set<Integer> s = new TreeSet<>();
for (int num : arr) {
s.add(num);
}
return s;
}
// Produces and returns a "deep copy" of the parameter map, which has the same
// structure and values as the parameter, but with all internal data structures
// and values copied. After calling this method, modifying the parameter or
// return value should NOT affect the other.
//
// Parameters:
// inputMap - the map to duplicate
//
// Returns:
// A deep copy of the parameter map.
public static Map<String, Set<Integer>> deepCopy(Map<String, Set<Integer>> inputMap) {
Map<String, Set<Integer>> deepCopy = new TreeMap<>();
for (String key : inputMap.keySet()) {
Set<Integer> inputSet = new TreeSet<>(inputMap.get(key));
// Set<Integer> inputSet = inputMap.get(key);
deepCopy.put(key, inputSet);
}
return deepCopy;
}
}

View File

@@ -0,0 +1,52 @@
import java.util.*;
public class InvertedIndex {
public static void main(String[] args) {
List<String> docs = new ArrayList<>();
docs.add("Raiders of the Lost Ark");
docs.add("The Temple of Doom");
docs.add("The Last Crusade");
Map<String, Set<String>> result = createIndex(docs);
System.out.println(docs);
System.out.println();
System.out.println(result);
}
// TODO: Write and document your createIndex method here
public static Map<String, Set<String>> createIndex(List<String> docs) {
Map<String, Set<String>> index = new TreeMap<>();
Set<String> uniqueWords = getUniqueWords(docs);
for (String uniqueWord : uniqueWords) {
index.put(uniqueWord.toLowerCase(), new HashSet<String>());
}
for (String word : index.keySet()) {
for (int i = 0; i < docs.size(); i++) {
Scanner wordScanner = new Scanner(docs.get(i));
while (wordScanner.hasNext()) {
if (wordScanner.next().equalsIgnoreCase(word)) {
index.get(word).add(docs.get(i));
}
}
}
}
return index;
}
public static Set<String> getUniqueWords(List<String> docs) {
Set<String> uniqueWords = new HashSet<>();
for (String title : docs) {
Scanner titleScanner = new Scanner(title);
while (titleScanner.hasNext()) {
uniqueWords.add(titleScanner.next());
}
titleScanner.close();
}
return uniqueWords;
}
}

41
debugging/Media.java Normal file
View File

@@ -0,0 +1,41 @@
import java.util.*;
/**
* An interface to represent various types of media (movies, books, tv shows, songs, etc.).
*/
public interface Media {
/**
* Gets the title of this media.
*
* @return The title of this media.
*/
public String getTitle();
/**
* Gets all artists associated with this media.
*
* @return A list of artists for this media.
*/
public List<String> getArtists();
/**
* Adds a rating to this media.
*
* @param score The score for the new rating. Should be non-negative.
*/
public void addRating(int score);
/**
* Gets the number of times this media has been rated.
*
* @return The number of ratings for this media.
*/
public int getNumRatings();
/**
* Gets the average (mean) of all ratings for this media.
*
* @return The average (mean) of all ratings for this media. If no ratings exist, returns 0.
*/
public double getAverageRating();
}

40
debugging/Testing.java Normal file
View File

@@ -0,0 +1,40 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
public class Testing {
@Test
@DisplayName("EXAMPLE TEST CASE - createIndex Example")
public void firstCaseTest() {
List<String> documents = new ArrayList<>(List.of("The Bee Movie is great!",
"I love the Bee Movie",
"Y'all seen Dune 2?"));
Map<String, Set<String>> index = InvertedIndex.createIndex(documents);
// Make sure that tokens are correctly converted to lower case
assertTrue(index.containsKey("bee"));
assertFalse(index.containsKey("Bee"));
// Make sure that punctuation is ignored
assertTrue(index.containsKey("great!"));
assertFalse(index.containsKey("great"));
// Check one of the sets
assertEquals(Set.of("The Bee Movie is great!",
"I love the Bee Movie"),
index.get("movie"));
}
@Test
@DisplayName("EXAMPLE TEST CASE - Book 2 String constructor + getters")
public void secondCaseTest() {
Book b = new Book("Title", "Author");
// Test all getters after constructing with 2 Strings
assertEquals("Title", b.getTitle());
assertEquals(List.of("Author"), b.getArtists());
assertEquals(0, b.getNumRatings());
assertEquals(0.0, b.getAverageRating());
}
}

BIN
in_class/cse123-inclass11/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,79 @@
import java.util.*;
import java.io.*;
public class Scrabble {
public static void main(String[] args) {
Set<String> dictionary = loadDictionary();
List<Character> letters = readLetters();
System.out.println("Searching " + letters);
findWords(letters, dictionary);
}
public static void findWords(List<Character> letters, Set<String> dict) {
findWords(letters, dict, "");
}
private static void findWords(List<Character> letters, Set<String> dict, String soFar) {
if (dict.contains(soFar)) {
System.out.println(soFar);
}
if (!letters.isEmpty()) {
for (int i = 0; i < letters.size(); i++) {
char letter = letters.remove(i);
findWords(letters, dict, soFar+letter);
letters.add(i, letter);
}
}
}
// HELPER METHODS
// does not check if word is legal
private static int scoreWord(String word) {
// A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
int[] values = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10};
int score = 0;
word = word.toUpperCase();
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
score += values[ch - 'A'];
}
return score;
}
private static List<Character> readLetters() {
List<Character> result = new ArrayList<>();
Scanner input = new Scanner(System.in);
System.out.print("Enter letters separated by whitespace: ");
Scanner letters = new Scanner(input.nextLine());
while (letters.hasNext()) {
String letter = letters.next();
if (letter.length() > 1) {
System.out.println(" Skipping " + letter + " - not one letter");
} else {
result.add(letter.toUpperCase().charAt(0));
}
}
return result;
}
private static Set<String> loadDictionary() {
try {
Scanner input = new Scanner(new File("dictionary.txt"));
Set<String> dict = new HashSet<>();
while (input.hasNextLine()) {
dict.add(input.nextLine());
}
return dict;
} catch (Exception e) {
return null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,67 @@
import java.util.*;
import java.io.*;
public class Scrabble {
public static void main(String[] args) {
Set<String> dictionary = loadDictionary();
List<Character> letters = readLetters();
String best = findBestWord(letters, dictionary);
System.out.println(best + " (" + scoreWord(best) + ")");
}
public static String findBestWord(List<Character> letters, Set<String> dict) {
}
// HELPER METHODS
// does not check if word is legal
private static int scoreWord(String word) {
// A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
int[] values = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10};
int score = 0;
word = word.toUpperCase();
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
score += values[ch - 'A'];
}
return score;
}
private static List<Character> readLetters() {
List<Character> result = new ArrayList<>();
Scanner input = new Scanner(System.in);
System.out.print("Enter letters separated by whitespace: ");
Scanner letters = new Scanner(input.nextLine());
while (letters.hasNext()) {
String letter = letters.next();
if (letter.length() > 1) {
System.out.println(" Skipping " + letter + " - not one letter");
} else {
result.add(letter.toUpperCase().charAt(0));
}
}
return result;
}
private static Set<String> loadDictionary() {
try {
Scanner input = new Scanner(new File("dictionary.txt"));
Set<String> dict = new HashSet<>();
while (input.hasNextLine()) {
dict.add(input.nextLine());
}
return dict;
} catch (Exception e) {
return null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
public class IsPalindromeArray {
public static void main(String[] args) {
System.out.println(isPalindrome(new int[] { 1, 2, 3, 4, 5, 4, 3, 2, 1 }));
System.out.println(isPalindrome(new int[] { 10, 20, 20, 10 }));
System.out.println(isPalindrome(new int[] { 1 }));
System.out.println(isPalindrome(new int[] {}));
System.out.println(isPalindrome(new int[] { 1, 2, 3, 4, 5 }));
System.out.println(isPalindrome(new int[] { 1, 2, 5, 3 }));
System.out.println(isPalindrome(new int[] { 10, 20, 30, 10 }));
}
public static boolean isPalindrome(int[] array) {
return isPalindrome(array, 0, array.length - 1);
}
private static boolean isPalindrome(int[] array, int lower, int upper) {
if (upper - lower < 1) {
return true;
} else if (array[lower] != array[upper]) {
return false;
} else {
return isPalindrome(array, lower+1,upper-1);
}
}
}

View File

@@ -0,0 +1,19 @@
public class IsPalindromeString {
public static void main(String[] args) {
System.out.println(isPalindrome("racecar"));
System.out.println(isPalindrome("tacocat"));
System.out.println(isPalindrome("ABBA"));
System.out.println(isPalindrome("x"));
System.out.println(isPalindrome(""));
System.out.println(isPalindrome("banana"));
System.out.println(isPalindrome("abcbba"));
System.out.println(isPalindrome("bramb"));
}
public static boolean isPalindrome(String s) {
if (s.length() <= 1 || s.equals(ReverseString.reverse(s))) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,38 @@
public class RecursiveMystery {
public static void main(String[] args) {
// mystery1(3);
// mystery2(4);
mystery3("taco");
}
public static void mystery1(int n) {
if (n <= 1) {
System.out.print(n);
} else {
mystery1(n / 2);
System.out.print(", " + n);
}
}
public static void mystery2(int n) {
if (n <= 0) {
System.out.print("*");
} else if (n % 2 == 0) {
System.out.print("(");
mystery2(n - 1);
System.out.print(")");
} else {
System.out.print("[");
mystery2(n - 1);
System.out.print("]");
}
}
public static void mystery3(String str) {
if (!str.isEmpty()) {
System.out.print(str.charAt(0));
mystery3(str.substring(1));
System.out.print(str.charAt(0));
}
}
}

View File

@@ -0,0 +1,22 @@
public class ReverseString {
public static void main(String[] args){
System.out.println(reverse("Hello"));
System.out.println(reverse("nathan"));
System.out.println(reverse("racecar"));
System.out.println(reverse("taco"));
System.out.println(reverse("ABBA"));
System.out.println(reverse(""));
System.out.println(reverse("a"));
}
public static String reverse(String str){
if (str.isEmpty()) {
return "";
} else if (str.length() == 1) {
return str;
}
String backwards = reverse(str.substring(1)) + str.charAt(0);
return backwards;
}
}

91
p1/Client.java Normal file
View File

@@ -0,0 +1,91 @@
import java.util.*;
// A program to work with Mini-Git. Manages the state of repositories and allows for all
// operations defined in Mini-Git.
public class Client {
private static List<String> ops = new ArrayList<>();
public static void main(String[] args) {
Collections.addAll(ops, "create", "head", "history", "commit", "drop",
"synchronize", "quit");
Scanner console = new Scanner(System.in);
Map<String, Repository> repos = new HashMap<>();
String op = "";
String name = "";
intro();
while (!op.equalsIgnoreCase("quit")) {
System.out.println("Available repositories: ");
for (Repository repo : repos.values()) {
System.out.println("\t" + repo);
}
System.out.println("Operations: " + ops);
System.out.print("Enter operation and repository: ");
String[] input = console.nextLine().split("\\s+");
op = input[0];
name = input.length > 1 ? input[1] : "";
while (!ops.contains(op) || (!op.equalsIgnoreCase("create") &&
!op.equalsIgnoreCase("quit") &&
!repos.containsKey(name))) {
System.out.println(" **ERROR**: Operation or repository not recognized.");
System.out.print("Enter operation and repository: ");
input = console.nextLine().split("\\s+");
op = input[0];
name = input.length > 1 ? input[1] : "";
}
Repository currRepo = repos.get(name);
op = op.toLowerCase();
if (op.equalsIgnoreCase("create")) {
if (currRepo != null) {
System.out.println(" **ERROR**: Repository with that name already exists.");
} else {
Repository newRepo = new Repository(name);
repos.put(name, newRepo);
System.out.println(" New repository created: " + newRepo);
}
} else if (op.equalsIgnoreCase("head")) {
System.out.println(currRepo.getRepoHead());
} else if (op.equalsIgnoreCase("history")) {
System.out.print("How many commits back? ");
int nHist = console.nextInt();
console.nextLine();
System.out.println(currRepo.getHistory(nHist));
} else if (op.equalsIgnoreCase("commit")) {
System.out.print("Enter commit message: ");
String message = console.nextLine();
System.out.println(" New commit: " + currRepo.commit(message));
} else if (op.equalsIgnoreCase("drop")) {
System.out.print("Enter ID to drop: ");
String idDrop = console.nextLine();
if (currRepo.drop(idDrop)) {
System.out.println(" Successfully dropped " + idDrop);
} else {
System.out.println(" No commit dropped!");
}
} else if (op.equalsIgnoreCase("synchronize")) {
System.out.print("Which repository would you like to " +
"synchronize into the given one? ");
String repo = console.nextLine();
if (repo.equals(name)) {
System.out.println("Cannot synchronize the same repositories!");
} else if (!repos.containsKey(repo)) {
System.out.println("Repository does not exist!");
} else {
currRepo.synchronize(repos.get(repo));
}
}
System.out.println();
}
}
// Prints out an introduction to the Mini-Git test client.
public static void intro() {
System.out.println("Welcome to the Mini-Git test client!");
System.out.println("Use this program to test your Mini-Git repository implemenation.");
System.out.println("Make sure to test all operations in all cases --");
System.out.println("some cases are particularly tricky.");
System.out.println();
}
}

148
p1/ExampleTesting.java Normal file
View File

@@ -0,0 +1,148 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
public class Testing {
private Repository repo1;
private Repository repo2;
// Occurs before each of the individual test cases
// (creates new repos and resets commit ids)
@BeforeEach
public void setUp() {
repo1 = new Repository("repo1");
repo2 = new Repository("repo2");
Repository.Commit.resetIds();
}
@Test
@DisplayName("EXAMPLE TEST - getHistory()")
public void getHistory() throws InterruptedException {
// Initialize commit messages
String[] commitMessages = new String[]{"Initial commit.",
"Updated method documentation.",
"Removed unnecessary object creation."};
commitAll(repo1, commitMessages);
testHistory(repo1, 1, commitMessages);
}
@Test
@DisplayName("EXAMPLE TEST - drop() (empty case)")
public void testDropEmpty() {
assertFalse(repo1.drop("123"));
}
@Test
@DisplayName("EXAMPLE TEST - drop() (front case)")
public void testDropFront() throws InterruptedException {
// Initialize commit messages
commitAll(repo1, new String[]{"First commit"}); // ID "0"
commitAll(repo2, new String[]{"Added unit tests."}); // ID "1"
// Assert that repo1 successfully dropped "0"
assertTrue(repo1.drop("0"));
assertEquals(repo1.getRepoSize(), 0);
// Assert that repo2 does not drop "0" but drops "1"
// (Note that the commit ID increments regardless of the repository!)
assertFalse(repo2.drop("0"));
assertTrue(repo2.drop("1"));
assertEquals(repo2.getRepoSize(), 0);
}
@Test
@DisplayName("EXAMPLE TEST - synchronize() (one: [1, 2], two: [3, 4])")
public void testSynchronizeOne() throws InterruptedException {
// Initialize commit messages
commitAll(repo1, new String[]{"One", "Two"});
commitAll(repo2, new String[]{"Three", "Four"});
// Make sure both repos got exactly 2 commits each
assertEquals(2, repo1.getRepoSize());
assertEquals(2, repo2.getRepoSize());
// Synchronize repo2 into repo1
repo1.synchronize(repo2);
assertEquals(4, repo1.getRepoSize());
assertEquals(0, repo2.getRepoSize());
// Make sure the history of repo1 is correctly synchronized
testHistory(repo1, 4, new String[]{"One", "Two", "Three", "Four"});
}
// Commits all of the provided messages into the provided repo, making sure timestamps
// are correctly sequential (no ties). If used, make sure to include
// 'throws InterruptedException'
// much like we do with 'throws FileNotFoundException'. Example useage:
//
// repo1:
// head -> null
// To commit the messages "one", "two", "three", "four"
// commitAll(repo1, new String[]{"one", "two", "three", "four"})
// This results in the following after picture
// repo1:
// head -> "four" -> "three" -> "two" -> "one" -> null
//
// YOU DO NOT NEED TO UNDERSTAND HOW THIS METHOD WORKS TO USE IT!
// (this is why documentation is important!)
public void commitAll(Repository repo, String[] messages) throws InterruptedException {
// Commit all of the provided messages
for (String message : messages) {
int size = repo.getRepoSize();
repo.commit(message);
// Make sure exactly one commit was added to the repo
assertEquals(size + 1, repo.getRepoSize(),
String.format("Size not correctly updated after commiting message [%s]",
message));
// Sleep to guarantee that all commits have different time stamps
Thread.sleep(2);
}
}
// Makes sure the given repositories history is correct up to 'n' commits, checking against
// all commits made in order. Example useage:
//
// repo1:
// head -> "four" -> "three" -> "two" -> "one" -> null
// (Commits made in the order ["one", "two", "three", "four"])
// To test the getHistory() method up to n=3 commits this can be done with:
// testHistory(repo1, 3, new String[]{"one", "two", "three", "four"})
// Similarly, to test getHistory() up to n=4 commits you'd use:
// testHistory(repo1, 4, new String[]{"one", "two", "three", "four"})
//
// YOU DO NOT NEED TO UNDERSTAND HOW THIS METHOD WORKS TO USE IT!
// (this is why documentation is important!)
public void testHistory(Repository repo, int n, String[] allCommits) {
int totalCommits = repo.getRepoSize();
assertTrue(n <= totalCommits,
String.format("Provided n [%d] too big. Only [%d] commits",
n, totalCommits));
String[] nCommits = repo.getHistory(n).split("\n");
assertTrue(nCommits.length <= n,
String.format("getHistory(n) returned more than n [%d] commits", n));
assertTrue(nCommits.length <= allCommits.length,
String.format("Not enough expected commits to check against. " +
"Expected at least [%d]. Actual [%d]",
n, allCommits.length));
for (int i = 0; i < n; i++) {
String commit = nCommits[i];
// Old commit messages/ids are on the left and the more recent commit messages/ids are
// on the right so need to traverse from right to left
int backwardsIndex = totalCommits - 1 - i;
String commitMessage = allCommits[backwardsIndex];
assertTrue(commit.contains(commitMessage),
String.format("Commit [%s] doesn't contain expected message [%s]",
commit, commitMessage));
assertTrue(commit.contains("" + backwardsIndex),
String.format("Commit [%s] doesn't contain expected id [%d]",
commit, backwardsIndex));
}
}
}

241
p1/Repository.java Normal file
View File

@@ -0,0 +1,241 @@
import java.util.*;
import java.text.SimpleDateFormat;
public class Repository {
private String name;
private Commit head;
private int size;
public Repository(String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Repository needs a name!");
}
this.name = name;
}
public String getRepoHead() {
if (head == null) {
return null;
}
return head.id;
}
public int getRepoSize() {
Commit temp = head;
int size = 0;
while (temp != null) {
size++;
temp = temp.past;
}
this.size = size;
return size;
}
public String toString() {
if (head == null) {
return name + " - No commits";
}
return name + " - Current head: " + head.toString();
}
public boolean contains(String targetId) {
Commit temp = head;
while (temp != null) {
if (temp.id.equals(targetId)) {
return true;
}
temp = temp.past;
}
return false;
}
public String getHistory(int n) {
if (n <= 0) {
throw new IllegalArgumentException("do you want the history or not?");
}
Commit temp = head;
String history = "";
if (n > size) {
while (temp != null) {
history += temp.toString() + "\n";
temp = temp.past;
}
}
for (int i = 0; i < n && temp != null; i++) {
history += temp.toString() + "\n";
temp = temp.past;
}
return history;
}
public String commit(String message) {
if (head == null) {
head = new Commit(message);
return head.id;
}
Commit old = head;
head = new Commit(message, old);
size++;
return head.id;
}
public boolean drop(String targetId) {
// list is empty
if (head == null) {
return false;
}
size--;
Commit temp = head;
// front
if (temp.id.equals(targetId)) {
head = temp.past;
return true;
}
// middle
while (temp.past != null && temp.past.past != null) {
if (temp.past.id == targetId) {
temp.past = temp.past.past;
return true;
}
temp = temp.past;
}
// end
if (temp.past != null && temp.past.id.equals(targetId)) {
temp.past = null;
return true;
}
// if we get here, targetId was not found, return size to initial state
size++;
return false;
}
public void synchronize(Repository other) {
if (head == null && other.head == null) {
head = null;
} else if (head == null) {
head = other.head;
other.head = null;
} else if (other.head == null) {
other.head = null;
} else {
List<Commit> commitList = new ArrayList<>();
Comparator commitComparator = new Comparator<Commit>() {
public int compare(Commit one, Commit two) {
return Long.compare(one.timeStamp, two.timeStamp);
}
};
Commit temp = head;
while (temp != null) {
commitList.add(temp);
temp = temp.past;
}
temp = other.head;
while (temp != null) {
commitList.add(temp);
temp = temp.past;
}
commitList.sort(commitComparator);
Commit prev = null;
for (Commit commit : commitList) {
commit.past = prev;
prev = commit;
}
head = commitList.get(commitList.size() - 1);
other.head = null;
}
}
/**
* DO NOT MODIFY
* A class that represents a single commit in the repository.
* Commits are characterized by an identifier, a commit message,
* and the time that the commit was made. A commit also stores
* a reference to the immediately previous commit if it exists.
*
* Staff Note: You may notice that the comments in this
* class openly mention the fields of the class. This is fine
* because the fields of the Commit class are public. In general,
* be careful about revealing implementation details!
*/
public class Commit {
private static int currentCommitID;
/**
* The time, in milliseconds, at which this commit was created.
*/
public final long timeStamp;
/**
* A unique identifier for this commit.
*/
public final String id;
/**
* A message describing the changes made in this commit.
*/
public final String message;
/**
* A reference to the previous commit, if it exists. Otherwise, null.
*/
public Commit past;
/**
* Constructs a commit object. The unique identifier and timestamp
* are automatically generated.
* @param message A message describing the changes made in this commit.
* @param past A reference to the commit made immediately before this
* commit.
*/
public Commit(String message, Commit past) {
this.id = "" + currentCommitID++;
this.message = message;
this.timeStamp = System.currentTimeMillis();
this.past = past;
}
/**
* Constructs a commit object with no previous commit. The unique
* identifier and timestamp are automatically generated.
* @param message A message describing the changes made in this commit.
*/
public Commit(String message) {
this(message, null);
}
/**
* Returns a string representation of this commit. The string
* representation consists of this commit's unique identifier,
* timestamp, and message, in the following form:
* "[identifier] at [timestamp]: [message]"
* @return The string representation of this collection.
*/
@Override
public String toString() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
Date date = new Date(timeStamp);
return id + " at " + formatter.format(date) + ": " + message;
}
/**
* Resets the IDs of the commit nodes such that they reset to 0.
* Primarily for testing purposes.
*/
public static void resetIds() {
Commit.currentCommitID = 0;
}
}
}

99
p1/Testing.java Normal file
View File

@@ -0,0 +1,99 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
public class Testing {
private Repository repo1;
private Repository repo2;
// Occurs before each of the individual test cases
// (creates new repos and resets commit ids)
@BeforeEach
public void setUp() {
repo1 = new Repository("repo1");
repo2 = new Repository("repo2");
Repository.Commit.resetIds();
}
// TODO: Write your tests here!
/////////////////////////////////////////////////////////////////////////////////
// PROVIDED HELPER METHODS (You don't have to use these if you don't want to!) //
/////////////////////////////////////////////////////////////////////////////////
// Commits all of the provided messages into the provided repo, making sure timestamps
// are correctly sequential (no ties). If used, make sure to include
// 'throws InterruptedException'
// much like we do with 'throws FileNotFoundException'. Example useage:
//
// repo1:
// head -> null
// To commit the messages "one", "two", "three", "four"
// commitAll(repo1, new String[]{"one", "two", "three", "four"})
// This results in the following after picture
// repo1:
// head -> "four" -> "three" -> "two" -> "one" -> null
//
// YOU DO NOT NEED TO UNDERSTAND HOW THIS METHOD WORKS TO USE IT! (this is why documentation
// is important!)
public void commitAll(Repository repo, String[] messages) throws InterruptedException {
// Commit all of the provided messages
for (String message : messages) {
int size = repo.getRepoSize();
repo.commit(message);
// Make sure exactly one commit was added to the repo
assertEquals(size + 1, repo.getRepoSize(),
String.format("Size not correctly updated after commiting message [%s]",
message));
// Sleep to guarantee that all commits have different time stamps
Thread.sleep(2);
}
}
// Makes sure the given repositories history is correct up to 'n' commits, checking against
// all commits made in order. Example useage:
//
// repo1:
// head -> "four" -> "three" -> "two" -> "one" -> null
// (Commits made in the order ["one", "two", "three", "four"])
// To test the getHistory() method up to n=3 commits this can be done with:
// testHistory(repo1, 3, new String[]{"one", "two", "three", "four"})
// Similarly, to test getHistory() up to n=4 commits you'd use:
// testHistory(repo1, 4, new String[]{"one", "two", "three", "four"})
//
// YOU DO NOT NEED TO UNDERSTAND HOW THIS METHOD WORKS TO USE IT! (this is why documentation
// is important!)
public void testHistory(Repository repo, int n, String[] allCommits) {
int totalCommits = repo.getRepoSize();
assertTrue(n <= totalCommits,
String.format("Provided n [%d] too big. Only [%d] commits",
n, totalCommits));
String[] nCommits = repo.getHistory(n).split("\n");
assertTrue(nCommits.length <= n,
String.format("getHistory(n) returned more than n [%d] commits", n));
assertTrue(nCommits.length <= allCommits.length,
String.format("Not enough expected commits to check against. " +
"Expected at least [%d]. Actual [%d]",
n, allCommits.length));
for (int i = 0; i < n; i++) {
String commit = nCommits[i];
// Old commit messages/ids are on the left and the more recent commit messages/ids are
// on the right so need to traverse from right to left
int backwardsIndex = totalCommits - 1 - i;
String commitMessage = allCommits[backwardsIndex];
assertTrue(commit.contains(commitMessage),
String.format("Commit [%s] doesn't contain expected message [%s]",
commit, commitMessage));
assertTrue(commit.contains("" + backwardsIndex),
String.format("Commit [%s] doesn't contain expected id [%d]",
commit, backwardsIndex));
}
}
}

View File

@@ -0,0 +1,128 @@
import java.util.*;
/**
* The Allocation class represents an unmodifiable relief solution.
* It provides methods to retrieve the total cost and total helped population
* of the solution. The ordering of the regions in the solution determines
* the population that can be helped.
*/
public class Allocation {
private List<Region> regions;
/**
* Creates a new Allocation object representing the given regions.
* @param regions the regions in the solution
*/
private Allocation(List<Region> regions) {
this.regions = new ArrayList<>(regions);
}
/**
* Creates a new Allocation object with no regions in it.
*/
public Allocation() {
this(new ArrayList<>());
}
/**
* Returns a copy of this allocation's regions.
*/
public List<Region> getRegions() {
return new ArrayList<>(regions);
}
/**
* Returns a new Allocation with the contents of this allocation
* and the passed in region added to it.
* @param r Region to be added to the end of the new Allocation.
* @return a new Allocation with r added to it.
*/
public Allocation withRegion(Region r) {
if (regions.contains(r)) {
throw new IllegalArgumentException("Allocation already contains region " + r);
}
List<Region> newRegions = new ArrayList<>(regions);
newRegions.add(r);
return new Allocation(newRegions);
}
/**
* Returns a new Allocation with the contents of this allocation
* and the passed in region removed from it.
* @param r Region to be removed from the new Allocation.
* @return a new Allocation with r removed from it.
*/
public Allocation withoutRegion(Region r) {
if (!regions.contains(r)) {
throw new IllegalArgumentException("Allocation doesn't contain region " + r);
}
List<Region> newRegions = new ArrayList<>(regions);
newRegions.remove(r);
return new Allocation(newRegions);
}
/**
* Returns the number of regions in this Allocation.
*/
public int size() {
return regions.size();
}
/**
* Calculates and returns the total population that can be helped
* by this Allocation.
* @return the total population that can be helped by this Allocation.
*/
public int totalPeople() {
int total = 0;
for (Region r : regions) {
total += r.getPopulation();
}
return total;
}
/**
* Calculates and returns the combined cost of this Allocation.
* @return the combined cost of this Allocation.
*/
public double totalCost() {
double total = 0;
for (int i = 0; i < regions.size(); i++) {
total += regions.get(i).getCost(i);
}
return total;
}
/**
* Returns a String representation of an Allocation object in the format:
* "[Region, ..., Region]" where each Region is in its string representation.
* @return the String representation of an Allocation object
*/
public String toString() {
return regions.toString();
}
/**
* Compares the specified object with this allocation for equality. Returns true if the
* specified object is also an Allocation and the two Allocations have the same
* collection of regions.
* @param other object to be compared for equality with this allocation
* @return true if the specified object is equal to this allocation
*/
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof Allocation)) {
return false;
}
Allocation otherAlloc = (Allocation)other;
return this.regions.equals(otherAlloc.getRegions());
}
public int hashCode() {
return regions.hashCode();
}
}

View File

@@ -0,0 +1,73 @@
import java.util.*;
public class Client {
private static Random rand = new Random();
public static void main(String[] args) throws Exception {
// List<Region> scenario = createRandomScenario(10, 10, 100, 1000, 100000);
List<Region> scenario = createSimpleScenario();
System.out.println(scenario);
double budget = 2000;
Set<Allocation> allocations = generateOptions(budget, scenario);
printAllocations(allocations);
}
public static Set<Allocation> generateOptions(double budget, List<Region> sites) {
Set<Allocation> allocations = new HashSet<>();
return generateOptions(budget, sites, allocations);
}
private static Set<Allocation> generateOptions(double budget, List<Region> sites,
Set<Allocation> allocations) {
if (sites.isEmpty()) {
return allocations;
}
Region temp = sites.remove();
if (budget >= temp.baseCost) {
budget -= temp.baseCost;
}
}
///////////////////////////////////////////////////////////////////////////
// PROVIDED HELPER METHODS - **DO NOT MODIFY ANYTHING BELOW THIS LINE!** //
///////////////////////////////////////////////////////////////////////////
public static void printAllocations(Set<Allocation> allocations) {
System.out.println("All Allocations:");
for (Allocation a : allocations) {
System.out.println(" " + a);
}
}
public static List<Region> createRandomScenario(int numLocs, int minPop, int maxPop,
double minCostPer, double maxCostPer) {
List<Region> result = new ArrayList<>();
for (int i = 0; i < numLocs; i++) {
int pop = rand.nextInt(minPop, maxPop + 1);
double cost = rand.nextDouble(minCostPer, maxCostPer) * pop;
result.add(new Region("Region #" + i, pop, round2(cost)));
}
return result;
}
public static List<Region> createSimpleScenario() {
List<Region> result = new ArrayList<>();
result.add(new Region("Region #1", 50, 500));
result.add(new Region("Region #2", 100, 700));
result.add(new Region("Region #3", 60, 1000));
result.add(new Region("Region #4", 20, 1000));
result.add(new Region("Region #5", 200, 900));
return result;
}
private static double round2(double num) {
return Math.round(num * 100) / 100.0;
}
}

View File

@@ -0,0 +1,84 @@
/**
* The Region class represents a geographical location with a name, population, and cost.
* It provides methods to retrieve the population and cost of the location,
* as well as a method to generate a string representation of the object.
*/
public class Region {
private String name;
private int population;
private double baseCost;
/**
* Creates a new Region object with the given name, population, and cost.
* @param name the name of the location
* @param pop the population of the location
* @param baseCost the base cost of the location
*/
public Region(String name, int pop, double baseCost) {
this.name = name;
this.population = pop;
this.baseCost = baseCost;
}
/**
* Returns the population of the location
* @return the population of the location
*/
public int getPopulation() { return this.population; }
/**
* Returns the cost of the location
* @param index a number indicating when this region is provided relief. A larger value for
* index indicates that this region is helped later.
* @return the cost of providing relief to this region. Regions that are
* helped later (i.e. with a larger index value) have a higher cost.
*/
public double getCost(int index) {
return (1 + 0.1 * index) * this.baseCost;
}
/**
* Returns a String representation of a Region object in the format:
* "<name>: pop. <population>, cost: $<cost>"
* @return the String representation of a Region object
*/
public String toString() {
return name + ": pop. " + population + ", base cost: $" + baseCost;
}
/**
* Compares the specified object with this location for equality. Returns true if the
* specified object is also a location and the two locations have the
* same name, population, and cost.
* @param other object to be compared for equality with this location
* @return true if the specified object is equal to this location
*/
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof Region)) {
return false;
}
Region otherLoc = (Region)other;
return this.name.equals(otherLoc.name) &&
this.population == otherLoc.population &&
this.baseCost == otherLoc.baseCost;
}
/**
* Returns the hash code value for this location
* @return the hash code value for this location
*/
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + Integer.hashCode(population);
result = 31 * result + Double.hashCode(baseCost);
return result;
}
}

100
p3/BitInputStream.java Normal file
View File

@@ -0,0 +1,100 @@
// The BitOutputStream and BitInputStream classes provide the ability to
// write and read individual bits to a file in a compact form. One major
// limitation of this approach is that the resulting file will always have
// a number of bits that is a multiple of 8. In effect, whatever bits are
// output to the file are padded at the end with 0's to make the total
// number of bits a multiple of 8.
//
// BitInputStream has the following public methods:
// public BitInputStream(String file)
// opens an input stream with the given file name
// public int nextBit()
// reads the next bit from input (throws exception if at end of file)
// public boolean hasNextBit()
// returns true if there's another bit in the input stream to be read
// public void close()
// closes the input
import java.io.*;
import java.util.*;
@SuppressWarnings("deprecation")
public class BitInputStream {
private FileInputStream input;
private int currentByte; // current set of bits (buffer)
private int nextByte; // next set of bits (buffer)
private int numBits; // how many bits from buffer have been used
private int remainingAtEnd; // how many bits will be remaining at the end
// after we're done
private static final int BYTE_SIZE = 8; // bits per byte
// pre : given file name is legal
// post: creates a BitInputStream reading input from the file
public BitInputStream(String file) {
try {
this.input = new FileInputStream(file);
// Read in the number of remaining bits at the end
this.remainingAtEnd = this.input.read();
// Set up the nextByte field.
this.nextByte = this.input.read();
} catch (IOException ex) {
throw new RuntimeException(ex.toString());
}
this.nextByte();
}
public boolean hasNextBit() {
boolean atEnd = this.currentByte == -1;
boolean onlyRemaining = this.nextByte == -1
&& BYTE_SIZE - this.numBits == this.remainingAtEnd;
return !atEnd && !onlyRemaining;
}
// post: reads next bit from input
// throws NoSuchElementException if there is no bit to return
public int nextBit() {
// if at eof, throw exception
if (!this.hasNextBit()) {
throw new NoSuchElementException();
}
int result = this.currentByte % 2;
this.currentByte /= 2;
this.numBits++;
if (this.numBits == BYTE_SIZE) {
this.nextByte();
}
return result;
}
// post: refreshes the internal buffer with the next BYTE_SIZE bits
private void nextByte() {
this.currentByte = this.nextByte;
if (this.currentByte != -1) {
try {
this.nextByte = this.input.read();
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
this.numBits = 0;
}
// post: input is closed
public void close() {
try {
this.input.close();
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
// included to ensure that the stream is closed
protected void finalize() {
this.close();
}
}

74
p3/BitOutputStream.java Normal file
View File

@@ -0,0 +1,74 @@
// The BitOutputStream and BitInputStream classes provide the ability to
// write and read individual bits to a file in a compact form. One major
// limitation of this approach is that the resulting file will always have
// a number of bits that is a multiple of 8. In effect, whatever bits are
// output to the file are padded at the end with 0's to make the total
// number of bits a multiple of 8.
import java.io.*;
import java.util.*;
@SuppressWarnings("deprecation")
public class BitOutputStream {
private PrintStream output;
private List<Integer> buffer;
private int currentByte; // a buffer used to build up next set of digits
private int numBits; // how many digits are currently in the buffer
private boolean debug; // set to true to write ASCII 0s and 1s rather than
// bits
private static final int BYTE_SIZE = 8; // digits per byte
// Creates a BitOutputStream sending output to the given stream. If debug
// is set to true, bits are printed as ASCII 0s and 1s.
public BitOutputStream(PrintStream output, boolean debug) {
this.buffer = new ArrayList<Integer>();
this.output = output;
this.debug = debug;
}
// Writes given bit to output
public void write(int bit) {
if (this.debug) {
System.out.print(bit);
}
if (bit < 0 || bit > 1) {
throw new IllegalArgumentException("Illegal bit: " + bit);
}
this.currentByte += bit << this.numBits;
this.numBits++;
if (this.numBits == BYTE_SIZE) {
this.buffer.add(this.currentByte);
this.numBits = 0;
this.currentByte = 0;
}
}
// post: output is closed
public void close() {
int remaining = BYTE_SIZE - this.numBits;
if (remaining == 8) {
remaining = 0;
}
/* Flush the last byte (if there is one) */
if (remaining > 0) {
this.buffer.add(this.currentByte);
}
/* Now that we've received all the output, prepend it with the number
* of missing bits from the end.
*/
this.output.write(remaining);
for (int b : this.buffer) {
this.output.write(b);
}
this.output.close();
}
// included to ensure that the stream is closed
protected void finalize() {
this.close();
}
}

117
p3/HuffmanCode.java Normal file
View File

@@ -0,0 +1,117 @@
import java.util.*;
import java.io.*;
public class HuffmanCode {
public HuffmanNode root;
// Constructor that builds the Huffman tree from an array of frequencies
public HuffmanCode(int[] frequencies) {
Queue<HuffmanNode> nodes = new PriorityQueue<>();
for (int i = 0; i < frequencies.length; i++) {
if (frequencies[i] > 0) {
nodes.add(new HuffmanNode((char) i, frequencies[i]));
}
}
while (nodes.size() > 1) {
HuffmanNode left = nodes.remove();
HuffmanNode right = nodes.remove();
HuffmanNode branch = new HuffmanNode('\0', left.freq + right.freq, left, right);
nodes.add(branch);
}
this.root = nodes.isEmpty() ? null : nodes.remove();
}
// Constructor that reads the Huffman tree from a Scanner
public HuffmanCode(Scanner input) {
this.root = new HuffmanNode('\0', 0); // Initialize root with a dummy node
while (input.hasNextLine()) {
int character = Integer.parseInt(input.nextLine());
String path = input.nextLine();
addNode(character, path);
}
}
// Adds a node to the Huffman tree based on the given path
private void addNode(int character, String path) {
HuffmanNode current = root;
for (int i = 0; i < path.length(); i++) {
char direction = path.charAt(i);
if (direction == '0') {
if (current.left == null) {
if (i == path.length() - 1) {
current.left = new HuffmanNode((char) character, 1); // Final node
} else {
current.left = new HuffmanNode('\0', 0); // Create a dummy node if it doesn't exist
}
}
current = current.left;
} else if (direction == '1') {
if (current.right == null) {
if (i == path.length() - 1) {
current.right = new HuffmanNode((char) character, 1); // Final node
} else {
current.right = new HuffmanNode('\0', 0); // Create a dummy node if it doesn't exist
}
}
current = current.right;
}
}
}
// Saves the Huffman tree to the given output stream in standard format
public void save(PrintStream output) {
save(output, this.root, "");
}
private void save(PrintStream output, HuffmanNode head, String path) {
if (head != null) {
if (head.data != '\0') {
output.println((int) head.data);
output.println(path);
}
save(output, head.left, path + "0");
save(output, head.right, path + "1");
}
}
// Translates bits from the input stream to characters and writes them to the output stream
public void translate(BitInputStream input, PrintStream output) {
HuffmanNode current = root;
while (input.hasNextBit()) {
int bit = input.nextBit();
if (bit == 0) {
current = current.left;
} else {
current = current.right;
}
if (current.left == null && current.right == null) { // Leaf node
output.print(current.data);
current = root; // Restart for next character
}
}
}
// Inner class representing a node in the Huffman tree
private static class HuffmanNode implements Comparable<HuffmanNode> {
public char data;
public int freq;
public HuffmanNode left;
public HuffmanNode right;
public HuffmanNode(char data, int frequency) {
this(data, frequency, null, null);
}
public HuffmanNode(char data, int frequency, HuffmanNode left, HuffmanNode right) {
this.data = data;
this.freq = frequency;
this.left = left;
this.right = right;
}
public int compareTo(HuffmanNode other) {
return Integer.compare(this.freq, other.freq);
}
}
}

80
p3/HuffmanCode.java.bak Normal file
View File

@@ -0,0 +1,80 @@
import java.util.*;
import java.io.*;
public class HuffmanCode {
public HuffmanNode root;
public HuffmanCode(int[] frequencies) {
Queue<HuffmanNode> nodes = new PriorityQueue<>();
for (int i=0;i<frequencies.length;i++) {
nodes.add(new HuffmanNode((char) i, frequencies[i]));
}
while (nodes.size()>1) {
HuffmanNode left = nodes.remove();
HuffmanNode right = nodes.remove();
HuffmanNode branch = new HuffmanNode(null, left.freq + right.freq, left, right);
nodes.add(branch);
}
this.root = nodes.remove();
}
public HuffmanCode(Scanner input) {
assembleTree(input, new HuffmanNode(null, null));
}
private assembleTree(Scanner input, HuffmanNode head) {
if (input.hasNextLine()) {
char character = (char) input.nextLine();
}
if (input.hasNextLine()) {
String loc = input.nextLine();
}
if (head == null) {
head = new HuffmanNode(null, null);
}
}
public void save(PrintStream output) {
save(output, this.root)
}
private void save(PrintStream output, HuffmanNode head) {
output.println((int) head.data);
output.println() ;
save(head.left);
save(head.right);
}
public void translate(BitInputStream input, PrintStream output) {
}
private static class HuffmanNode implements Comparable<HuffmanNode> {
public final char data;
public final int freq;
public HuffmanNode left;
public HuffmanNode right;
public HuffmanNode(char data, int frequency) {
this(data, frequency, null, null);
}
public HuffmanNode(char data, int frequency, HuffmanNode left, HuffmanNode right) {
this.data = data;
this.freq = freq;
this.left = left;
this.right = right;
}
public int compareTo(HuffmanNode other) {
if (this.freq == other.freq) {
return 0;
}
else if (this.freq < other.freq) {
return -1;
}
return 1;
}
}
}

209
p3/HuffmanCompressor.java Normal file
View File

@@ -0,0 +1,209 @@
import java.io.*;
import java.util.*;
public class HuffmanCompressor {
private String filename;
public static final int CHAR_MAX = 255; // max char value
public static final int MAKE_CODE = 1;
public static final int COMPRESS = 2;
public static final int DECOMPRESS = 3;
public static final int ROUND_TRIP = 4;
public HuffmanCompressor(String filename) {
if (!filename.endsWith(".txt")) {
throw new IllegalArgumentException(
"This compressor only works on text files!");
}
this.filename = filename.split(".txt")[0];
}
public void makeCode() throws IOException {
System.out.println("I am about to make the Huffman code for "
+ filename + ".txt...");
FileInputStream input = new FileInputStream(filename + ".txt");
int[] count = new int[CHAR_MAX];
int n = input.read();
while (n != -1) {
int prev = n;
count[n]++;
n = input.read();
// Remove added newline by ed
if (n == -1 && prev == 10) {
count[prev]--;
}
}
// Build the code; open the output file; save the code
System.out.println("\tI built up a frequency table of the " +
"characters in your file.");
System.out.println("\tNow, I'm going to call your HuffmanCode(int[]) " +
"constructor using that frequency table.");
HuffmanCode t = new HuffmanCode(count);
System.out.println("\tOkay! Now, I am going to save the code (using " +
"your save method) to the file " + filename +
".code!");
PrintStream output = new PrintStream(new File(filename + ".code"));
t.save(output);
System.out.println("...I am done making the Huffman Code!");
}
public void compress(boolean debug) throws IOException {
System.out.println("I am about to attempt to COMPRESS "
+ filename + ".txt:");
/* We must make the code before we can compress... */
this.makeCode();
System.out.println("\tNow that I have the Huffman Code, I am going " +
"to use the huffman code file created by");
System.out.println("\tyour save() method to compress the contents " +
"into " + filename + ".short!");
String[] codes = new String[CHAR_MAX];
Scanner codeInput = new Scanner(new File(filename + ".code"));
while (codeInput.hasNextLine()) {
int n = Integer.parseInt(codeInput.nextLine());
codes[n] = codeInput.nextLine();
}
// Open file to be compressed; open output file
FileInputStream input = new FileInputStream(this.filename + ".txt");
BitOutputStream output = new BitOutputStream(
new PrintStream(this.filename + ".short"), debug);
// Print out the debug binary to a .debug file
FileOutputStream debugOut = null;
if (debug) {
debugOut = new FileOutputStream(this.filename + ".debug");
}
// Do the compression
int n = input.read();
while (n != -1) {
if (codes[n] == null) {
// check if this is actually an error, or an ed added newline
if (!(n == 10 && input.read() == -1)) {
System.out.println("Your code file has no code for " + n +
" (the character '" + (char) n + "')");
System.out.println("exiting...");
System.exit(1);
}
n = -1;
} else {
for (int i = 0; i < codes[n].length(); i++) {
output.write(codes[n].charAt(i) - '0');
// Write to the .debug file
if (debugOut != null) {
debugOut.write(codes[n].charAt(i));
}
}
n = input.read();
}
}
input.close();
output.close();
System.out.println("...I am done compressing the file");
}
public void decompress(boolean printToConsole) throws IOException {
System.out.println("I am about to attempt to DECOMPRESS " +
filename + ".short:");
System.out.println("\tTo do this, I must first read in the huffman " +
"code used to compress the file.");
System.out.println("\tI will use your HuffmanCode(Scanner) " +
"constructor!");
// Open code file and construct tree
Scanner codeInput = new Scanner(new File(this.filename + ".code"));
HuffmanCode t = new HuffmanCode(codeInput);
// Open compressed file; open output
BitInputStream input = new BitInputStream(this.filename + ".short");
PrintStream output = System.out;
if (!printToConsole) {
System.out.println("\tNow, I will decompress the file using your " +
"translate() method and save");
System.out.println("\tthe output into " + this.filename + ".new");
output = new PrintStream(new File(this.filename + ".new"));
}
else {
System.out.println("\tNow, I will decompress the file using your " +
"translate() method and display it on the console");
}
// Decompress the file
t.translate(input, output);
output.close();
System.out.println("...I am done decompressing the file");
}
public static void main(String[] args) throws IOException {
Scanner console = new Scanner(System.in);
System.out.println("Welcome to the CSE 123 Huffman Compressor!");
System.out.println();
String filename;
do {
System.out.print("Which file would you like to work with (it " +
"must be a txt file)? ");
filename = console.nextLine().trim();
} while (!filename.endsWith(".txt"));
System.out.println();
int choice = -1;
while (choice == -1) {
System.out.println("Would you like to:");
System.out.println("\t(1) make a huffman code,");
System.out.println("\t(2) compress a file,");
System.out.println("\t(3) decompress a file, or");
System.out.println(
"\t(4) do a compression followed by a decompression");
System.out.print("1-4? ");
String choiceStr = console.nextLine().trim();
try {
choice = Integer.parseInt(choiceStr);
if (choice < 1 || choice > 4) {
choice = -1;
}
} catch(NumberFormatException e) {
/* Don't change choice; so, the loop will repeat. */
}
}
boolean debug = false;
if (choice == COMPRESS || choice == ROUND_TRIP) {
debug = prompt(
console,
"Would you like to debug the compressed file (y/n)? ");
}
boolean toConsole = false;
if (choice == DECOMPRESS || choice == ROUND_TRIP) {
toConsole = !prompt(
console,
"Would you like to print the result to a file (y/n)? ");
}
HuffmanCompressor huffman = new HuffmanCompressor(filename);
switch (choice) {
case 1: huffman.makeCode(); break;
case 2: huffman.compress(debug); break;
case 3: huffman.decompress(toConsole); break;
case 4:
huffman.compress(debug);
huffman.decompress(toConsole);
break;
}
}
public static boolean prompt(Scanner console, String message) {
System.out.print(message);
return console.nextLine().trim().toLowerCase().startsWith("y");
}
}

140
p3/hamlet.code Normal file
View File

@@ -0,0 +1,140 @@
91
0000000000
74
0000000001000
48
0000000001001000
52
0000000001001001
54
0000000001001010
34
00000000010010110
38
00000000010010111
49
00000000010011
58
000000000101
86
000000000110
85
000000000111
69
000000001
87
00000001
76
000000100
71
000000101
77
000000110
83
000000111
103
000001
117
00001
10
00010
107
0001100
66
000110100
89
0001101010
68
0001101011
45
000110110
82
0001101110
122
00011011110
41
000110111110
40
000110111111
118
0001110
39
0001111
97
0010
100
00110
99
001110
102
001111
111
0100
116
0101
119
011000
65
01100100
67
0110010100
120
0110010101
78
0110010110
75
0110010111
33
011001100
79
011001101
84
01100111
108
01101
44
011100
121
011101
46
011110
98
0111110
70
0111111000
106
01111110010
113
01111110011
59
011111101
73
01111111
32
10
101
1100
112
1101000
72
11010010
63
110100110
81
11010011100
93
11010011101
80
1101001111
109
110101
114
11011
105
11100
104
11101
110
11110
115
11111

BIN
p3/hamlet.short Normal file

Binary file not shown.

4463
p3/hamlet.txt Normal file

File diff suppressed because it is too large Load Diff

40
p3/short.code Normal file
View File

@@ -0,0 +1,40 @@
105
0000
99
0001
119
001000
10
0010010
109
0010011
102
00101
108
0011
110
0100
114
0101
97
0110
104
01110
112
01111
101
100
115
1010
116
1011
111
1100
100
11010
117
110110
121
110111
32
111

2
p3/short.txt Normal file
View File

@@ -0,0 +1,2 @@
this is a short input file composed entirely of spaces and lowercase letters
and end of line characters to help you test your code

82
p3/short256.code Normal file
View File

@@ -0,0 +1,82 @@
110
00000
109
0000100
237
00001010
119
00001011
249
000011
105
00010
227
00011
99
00100
108
00101
236
00110
238
00111
233
01000
97
01001
114
01010
242
01011
229
0110
101
0111
225
10000
104
100010
112
100011
240
100100
247
10010100
127
10010101
10
10010110
13
10010111
232
100110
138
1001110
117
1001111
115
10100
244
10101
100
101100
228
101101
116
10111
243
11000
239
11001
111
11010
121
1101100
230
1101101
102
1101110
245
1101111
32
111

BIN
p3/short256.short Normal file

Binary file not shown.

2
p3/short256.txt Normal file
View File

@@ -0,0 +1,2 @@
ôèéó éó á óèïòô éîðõô æéìå ãïíðïóåä åîôéòåìù ïæ óðáãåó áîä ìï÷åòãáóå ìåôôåòóŠáîä åîä ïæ ìéîå ãèáòáãôåòó ôï èåìð ùïõ ôåóô ùïõò ãïäåŠthis is a short input file composed entirely of spaces and lowercase letters
and end of line characters to help you test your code

View File

@@ -0,0 +1,8 @@
98
0
99
100
32
101
97
11

View File

@@ -0,0 +1 @@
aba ab cabbb

8
p3/taxi.code Normal file
View File

@@ -0,0 +1,8 @@
97
0
98
10
99
110
32
111

1
p3/taxi.short Normal file
View File

@@ -0,0 +1 @@
<06>>

1
p3/taxi.txt Normal file
View File

@@ -0,0 +1 @@
ab ab cab

6
p3/tiny.code Normal file
View File

@@ -0,0 +1,6 @@
97
0
99
10
98
11

BIN
p3/tiny.short Normal file

Binary file not shown.

1
p3/tiny.txt Normal file
View File

@@ -0,0 +1 @@
cbbaaa

88
search-engine/Book.java Normal file
View File

@@ -0,0 +1,88 @@
// Nik Johnson
// TA: Zachary Bi
// 4-2-2024
import java.util.*;
import java.text.DecimalFormat;
// book object, which can have a title, authors, and a list of ratings
public class Book implements Media {
private String title;
private String author;
private List<String> authors;
private List<Integer> ratings;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public Book(String title, List<String> authors) {
this.title = title;
this.authors = authors;
}
public String getTitle() {
return this.title;
}
public List<String> getArtists() {
List<String> artists = new ArrayList<>();
if (this.author != null) {
artists.add(this.author);
}
if (this.authors != null) {
for (String author : authors) {
artists.add(author);
}
}
return artists;
}
public void addRating(int score) {
if (this.ratings == null) {
ratings = new ArrayList<>();
}
this.ratings.add(score);
}
public int getNumRatings() {
if (this.ratings == null) {
return 0;
}
return this.ratings.size();
}
public double getAverageRating() {
if (this.ratings == null) {
return 0;
}
int sum = 0;
for (int rating : ratings) {
sum += rating;
}
return (double)sum / this.ratings.size();
}
public String toString() {
String output = "";
output += this.title + " by " + this.getArtists() + ": ";
DecimalFormat df = new DecimalFormat("#." + "0".repeat(2));
if (ratings != null) {
output += df.format(this.getAverageRating()) + " (" + (this.ratings.size()) + " ratings" + ")";
return output;
}
return output + "No ratings yet!";
}
}

41
search-engine/Media.java Normal file
View File

@@ -0,0 +1,41 @@
import java.util.*;
/**
* An interface to represent various types of media (movies, books, tv shows, songs, etc.).
*/
public interface Media {
/**
* Gets the title of this media.
*
* @return The title of this media.
*/
public String getTitle();
/**
* Gets all artists associated with this media.
*
* @return A list of artists for this media.
*/
public List<String> getArtists();
/**
* Adds a rating to this media.
*
* @param score The score for the new rating. Should be non-negative.
*/
public void addRating(int score);
/**
* Gets the number of times this media has been rated.
*
* @return The number of ratings for this media.
*/
public int getNumRatings();
/**
* Gets the average (mean) of all ratings for this media.
*
* @return The average (mean) of all ratings for this media. If no ratings exist, returns 0.
*/
public double getAverageRating();
}

View File

@@ -0,0 +1,213 @@
import java.io.*;
import java.util.*;
// Name: Niklas Johnson
// Date: 4-2-2024
// This class allows users to find and rate books within BOOK_DIRECTORY
// containing certain terms. Also handles omitting items if the query is prefixed
// with a '-'
public class SearchClient {
public static final String BOOK_DIRECTORY = "./books";
public static void main(String[] args) throws FileNotFoundException {
Scanner console = new Scanner(System.in);
List<Book> books = loadBooks();
List<Media> media = new ArrayList<>(books);
Map<String, Set<Media>> index = createIndex(media);
System.out.println("Welcome to the CSE 123 Search Engine!");
String command = "";
while (!command.equalsIgnoreCase("quit")) {
System.out.println("What would you like to do? [Search, Rate, Quit]");
System.out.print("> ");
command = console.nextLine();
if (command.equalsIgnoreCase("search")) {
searchQuery(console, index);
} else if (command.equalsIgnoreCase("rate")) {
addRating(console, media);
} else if (!command.equalsIgnoreCase("quit")) {
System.out.println("Invalid command, please try again.");
}
}
System.out.println("See you next time!");
}
// Produces and returns a "deep copy" of the parameter map, which has the same
// structure and values as the parameter, but with all internal data structures
// and values copied. After calling this method, modifying the parameter or
// return value should NOT affect the other.
//
// Parameters:
// inputMap - the map to duplicate
//
// Returns:
// A deep copy of the parameter map.
// TODO: Copy fixed version and update to use Media
public static Map<String, Set<Media>> deepCopy(Map<String, Set<Media>> inputMap) {
Map<String, Set<Media>> deepCopy = new TreeMap<>();
for (String key : inputMap.keySet()) {
Set<Media> inputSet = new HashSet<>(inputMap.get(key));
deepCopy.put(key, inputSet);
}
return deepCopy;
}
// creates a directory of words that appear in a given list of Strings
// takes a list of strings to process
// returns sorted map containing the index
// no thrown exceptions
public static Map<String, Set<Media>> createIndex(List<Media> media) throws FileNotFoundException {
Map<String, Set<Media>> index = new TreeMap<>();
Set<String> uniqueWords = getUniqueWords(media);
for (String uniqueWord : uniqueWords) {
index.put(uniqueWord.toLowerCase(), new HashSet<Media>());
}
for (String word : index.keySet()) {
for (int i = 0; i < media.size(); i++) {
Scanner titleScanner = new Scanner(media.get(i).getTitle());
while (titleScanner.hasNext()) {
if (titleScanner.next().equalsIgnoreCase(word)) {
index.get(word).add(media.get(i));
}
}
titleScanner.close();
for (String artists : media.get(i).getArtists()) {
Scanner artistScanner = new Scanner(artists);
while (artistScanner.hasNext()) {
if (artistScanner.next().equalsIgnoreCase(word)) {
index.get(word).add(media.get(i));
}
}
artistScanner.close();
}
}
}
return index;
}
// helper method for createIndex. looks through given list of strings,
// keeping track of only unique words
// takes list of words to prune
// returns pruned set of words
// no thrown exceptions
public static Set<String> getUniqueWords(List<Media> media) throws FileNotFoundException {
Set<String> uniqueWords = new HashSet<>();
for (Media thing : media) {
Scanner mediaScanner = new Scanner(new File(BOOK_DIRECTORY + "/" + thing.getTitle() + ".txt"));
while (mediaScanner.hasNext()) {
uniqueWords.add(mediaScanner.next());
}
mediaScanner.close();
}
return uniqueWords;
}
// Allows the user to search a specific query using the provided 'index' to find appropraite
// Media entries.
//
// Parameters:
// console - the Scanner to get user input from
// index - invertedIndex mapping terms to the Set of media containing those terms
public static void searchQuery(Scanner console, Map<String, Set<Media>> index) {
System.out.println("Enter query:");
System.out.print("> ");
String query = console.nextLine();
Optional<Set<Media>> result = search(deepCopy(index), query);
if (result.isEmpty()) {
System.out.println("\tNo results!");
} else {
for (Media m : result.get()) {
System.out.println("\t" + m.toString());
}
}
}
// Allows the user to add a rating to one of the options wthin 'media'
//
// Parameters:
// console - the Scanner to get user input from
// media - list of all media options loaded into the search engine
public static void addRating(Scanner console, List<Media> media) {
System.out.print("[" + media.get(0).getTitle());
for (int i = 1; i < media.size(); i++) {
System.out.print(", " + media.get(i).getTitle());
}
System.out.println("]");
System.out.println("What would you like to rate (enter index)?");
System.out.print("> ");
int choice = Integer.parseInt(console.nextLine());
if (choice < 0 || choice >= media.size()) {
System.out.println("Invalid choice");
} else {
System.out.println("Rating [" + media.get(choice).getTitle() + "]");
System.out.println("What rating would you give?");
System.out.print("> ");
int rating = Integer.parseInt(console.nextLine());
media.get(choice).addRating(rating);
}
}
// Searches a specific query using the provided 'index' to find appropraite Media entries.
// terms are determined by whitespace separation. If a term is proceeded by '-' any entry
// containing that term will be removed from the result.
//
// Parameters:
// index - invertedIndex mapping terms to the Set of media containing those terms
// query - user's entered query string to use in searching
//
// Returns:
// An optional set of all Media containing the requirested terms. If none, Optional.Empty()
public static Optional<Set<Media>> search(Map<String, Set<Media>> index, String query) {
Optional<Set<Media>> ret = Optional.empty();
Scanner tokens = new Scanner(query);
while (tokens.hasNext()) {
boolean minus = false;
String token = tokens.next().toLowerCase();
if (token.startsWith("-")) {
minus = true;
token = token.substring(1);
}
if (index.containsKey(token)) {
if (ret.isEmpty() && !minus) {
ret = Optional.of(index.get(token));
} else if (!ret.isEmpty() && minus) {
ret.get().removeAll(index.get(token));
} else if (!ret.isEmpty() && !minus) {
ret.get().retainAll(index.get(token));
}
}
}
return ret;
}
// Loads all books from BOOK_DIRECTORY. Assumes that each book starts with two lines -
// "Title: " which is followed by the book's title
// "Author: " which is followed by the book's author
//
// Returns:
// A list of all book objects corresponding to the ones located in BOOK_DIRECTORY
public static List<Book> loadBooks() throws FileNotFoundException {
List<Book> ret = new ArrayList<>();
File dir = new File(BOOK_DIRECTORY);
for (File f : dir.listFiles()) {
Scanner sc = new Scanner(f);
String title = sc.nextLine().substring("Title: ".length());
String author = sc.nextLine().substring("Author: ".length());
ret.add(new Book(title, author));
}
return ret;
}
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff