init
This commit is contained in:
41
Bomb.java
Normal file
41
Bomb.java
Normal 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"); }
|
||||
}
|
||||
}
|
||||
47
c1/AbstractStrategyGame.java
Normal file
47
c1/AbstractStrategyGame.java
Normal 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
36
c1/Client.java
Normal 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
117
c1/ConnectFour.java
Normal 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
53
c1/Testing.java
Normal 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
132
c1/TicTacToe.java
Normal 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;
|
||||
}
|
||||
}
|
||||
BIN
ciphers/4as0ufZh9PpgbDEj0M0prBKp
Normal file
BIN
ciphers/4as0ufZh9PpgbDEj0M0prBKp
Normal file
Binary file not shown.
49
ciphers/Cipher.java
Normal file
49
ciphers/Cipher.java
Normal 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
45
ciphers/Client.java
Normal 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);
|
||||
}
|
||||
}
|
||||
11
ciphers/SubstitutionRandom.java
Normal file
11
ciphers/SubstitutionRandom.java
Normal 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
19
ciphers/Testing.java
Normal 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!");
|
||||
}
|
||||
}
|
||||
1
ciphers/Transposition.java
Normal file
1
ciphers/Transposition.java
Normal file
@@ -0,0 +1 @@
|
||||
// TODO: Write your implementation to Transposition here!
|
||||
1
ciphers/Vigenere.java
Normal file
1
ciphers/Vigenere.java
Normal file
@@ -0,0 +1 @@
|
||||
// TODO: Write your implementation to Vigenere here!
|
||||
1
ciphers/files/ag.txt
Normal file
1
ciphers/files/ag.txt
Normal file
@@ -0,0 +1 @@
|
||||
HADBADCABBAGEBAG
|
||||
4463
ciphers/files/hamlet.txt
Normal file
4463
ciphers/files/hamlet.txt
Normal file
File diff suppressed because it is too large
Load Diff
15
ciphers/files/simple.txt
Normal file
15
ciphers/files/simple.txt
Normal 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
|
||||
BIN
ciphers/lib/junit-platform-console-standalone-1.10.2.jar
Normal file
BIN
ciphers/lib/junit-platform-console-standalone-1.10.2.jar
Normal file
Binary file not shown.
BIN
debugging/2XIGPFWWQh6rdNJQSyU34zis
Normal file
BIN
debugging/2XIGPFWWQh6rdNJQSyU34zis
Normal file
Binary file not shown.
71
debugging/Book.java
Normal file
71
debugging/Book.java
Normal 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
54
debugging/Debugging.java
Normal 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;
|
||||
}
|
||||
}
|
||||
52
debugging/InvertedIndex.java
Normal file
52
debugging/InvertedIndex.java
Normal 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
41
debugging/Media.java
Normal 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
40
debugging/Testing.java
Normal 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());
|
||||
}
|
||||
}
|
||||
21
p1/.vscode/launch.json
vendored
Normal file
21
p1/.vscode/launch.json
vendored
Normal 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": "p1_b4eddd44"
|
||||
}
|
||||
]
|
||||
}
|
||||
91
p1/Client.java
Normal file
91
p1/Client.java
Normal 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
148
p1/ExampleTesting.java
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
146
p1/Repository.java
Normal file
146
p1/Repository.java
Normal file
@@ -0,0 +1,146 @@
|
||||
import java.util.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
public class Repository {
|
||||
|
||||
private String name;
|
||||
private LinkedList<Commit> commits;
|
||||
|
||||
public Repository(String name) {
|
||||
if (name.isEmpty() || name == null) {
|
||||
throw new IllegalArgumentException("Repository must have a name!");
|
||||
}
|
||||
this.name = name;
|
||||
this.commits = new LinkedList<>();
|
||||
}
|
||||
|
||||
public String getRepoHead() {
|
||||
return commits.peek().id;
|
||||
}
|
||||
|
||||
public int getRepoSize() {
|
||||
return commits.size();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name + " - Current head: " + commits.peek().toString();
|
||||
}
|
||||
|
||||
public boolean contains(String targetId) {
|
||||
for (Commit commit : commits) {
|
||||
if (commit.id == targetId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getHistory(int n) {
|
||||
String output = "";
|
||||
for (int i = 0; i < commits.size() - 1; i++) {
|
||||
output += commits.get(i).toString() + "\n";
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public String commit(String message) {
|
||||
commits.add(new Commit(message));
|
||||
return commits.peek().id;
|
||||
}
|
||||
|
||||
public boolean drop(String targetId) {
|
||||
for (int n = 0; n < commits.size() - 1; n ++) {
|
||||
if (commits.get(n).id == targetId) {
|
||||
commits.remove(n);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void synchronize(Repository other) {
|
||||
|
||||
}
|
||||
/**
|
||||
* 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
99
p1/Testing.java
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
88
search-engine/Book.java
Normal file
88
search-engine/Book.java
Normal 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
41
search-engine/Media.java
Normal 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();
|
||||
}
|
||||
213
search-engine/SearchClient.java
Normal file
213
search-engine/SearchClient.java
Normal 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;
|
||||
}
|
||||
}
|
||||
BIN
search-engine/X5j9Zv1uw5KI5rEyO4djmOx4
Normal file
BIN
search-engine/X5j9Zv1uw5KI5rEyO4djmOx4
Normal file
Binary file not shown.
5168
search-engine/books/A Doll's House : a play.txt
Normal file
5168
search-engine/books/A Doll's House : a play.txt
Normal file
File diff suppressed because it is too large
Load Diff
3406
search-engine/books/Alice's Adventures in Wonderland.txt
Normal file
3406
search-engine/books/Alice's Adventures in Wonderland.txt
Normal file
File diff suppressed because it is too large
Load Diff
15470
search-engine/books/Dracula.txt
Normal file
15470
search-engine/books/Dracula.txt
Normal file
File diff suppressed because it is too large
Load Diff
7246
search-engine/books/Frankenstein; Or, The Modern Prometheus.txt
Normal file
7246
search-engine/books/Frankenstein; Or, The Modern Prometheus.txt
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
5270
search-engine/books/Romeo and Juliet.txt
Normal file
5270
search-engine/books/Romeo and Juliet.txt
Normal file
File diff suppressed because it is too large
Load Diff
6404
search-engine/books/The Great Gatsby.txt
Normal file
6404
search-engine/books/The Great Gatsby.txt
Normal file
File diff suppressed because it is too large
Load Diff
8527
search-engine/books/The Picture of Dorian Gray.txt
Normal file
8527
search-engine/books/The Picture of Dorian Gray.txt
Normal file
File diff suppressed because it is too large
Load Diff
6370
search-engine/books/The War of the Worlds.txt
Normal file
6370
search-engine/books/The War of the Worlds.txt
Normal file
File diff suppressed because it is too large
Load Diff
7494
search-engine/books/Treasure Island.txt
Normal file
7494
search-engine/books/Treasure Island.txt
Normal file
File diff suppressed because it is too large
Load Diff
3981
search-engine/books/Winnie-the-Pooh.txt
Normal file
3981
search-engine/books/Winnie-the-Pooh.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user