diff --git a/Programming Languages/Part C/.vscode/launch.json b/Programming Languages/Part C/.vscode/launch.json index 49ea47c..9cda636 100644 --- a/Programming Languages/Part C/.vscode/launch.json +++ b/Programming Languages/Part C/.vscode/launch.json @@ -8,7 +8,17 @@ "name": "Debug Local File", "type": "Ruby", "request": "launch", + "cwd": "${workspaceRoot}", "program": "${file}" + }, + { + "name": "Run Game", + "type": "Ruby", + "request": "launch", + "program": "${file}", + "args": [ + "enhanced" + ] } ] } \ No newline at end of file diff --git a/Programming Languages/Part C/.vscode/settings.json b/Programming Languages/Part C/.vscode/settings.json new file mode 100644 index 0000000..a16bdbc --- /dev/null +++ b/Programming Languages/Part C/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "code-runner.executorMap": { + "ruby": "irb -I . -r", + } +} \ No newline at end of file diff --git a/Programming Languages/Part C/Week 1/124_classes_objects.rb b/Programming Languages/Part C/Week 1/124_classes_objects.rb index 6900791..e953e28 100644 --- a/Programming Languages/Part C/Week 1/124_classes_objects.rb +++ b/Programming Languages/Part C/Week 1/124_classes_objects.rb @@ -5,7 +5,7 @@ class A def m1 34 end - + def m2 (x,y) z = 7 if x > y diff --git a/Programming Languages/Part C/Week 1/129_classes_dynamic.rb b/Programming Languages/Part C/Week 1/129_classes_dynamic.rb index c927c8f..bb71989 100644 --- a/Programming Languages/Part C/Week 1/129_classes_dynamic.rb +++ b/Programming Languages/Part C/Week 1/129_classes_dynamic.rb @@ -11,7 +11,7 @@ class MyRational end end -class Fixnum +class Integer def double self + self end diff --git a/Programming Languages/Part C/Week 1/hw6assignment.rb b/Programming Languages/Part C/Week 1/hw6assignment.rb new file mode 100644 index 0000000..5bbfa77 --- /dev/null +++ b/Programming Languages/Part C/Week 1/hw6assignment.rb @@ -0,0 +1,61 @@ +# University of Washington, Programming Languages, Homework 6, hw6runner.rb + +# This is the only file you turn in, so do not modify the other files as +# part of your solution. + +class MyPiece < Piece + # The constant All_My_Pieces should be declared here + All_Pieces = [[[[0, 0], [1, 0], [0, 1], [1, 1]]], # square (only needs one) + rotations([[0, 0], [-1, 0], [1, 0], [0, -1]]), # T + [[[0, 0], [-1, 0], [1, 0], [2, 0]], # long (only needs two) + [[0, 0], [0, -1], [0, 1], [0, 2]]], + rotations([[0, 0], [0, -1], [0, 1], [1, 1]]), # L + rotations([[0, 0], [0, -1], [0, 1], [-1, 1]]), # inverted L + rotations([[0, 0], [-1, 0], [0, -1], [1, -1]]), # S + rotations([[0, 0], [1, 0], [0, -1], [-1, -1]]), # Z + rotations([[0,0],[-1,0],[-1,-1],[0,-1],[1,-1]]), + rotations([[-2,0],[-1,0],[0,0],[1,0],[2,0]]), + rotations([[0,0],[1,0],[0,1],[0,1]]) + ] + # your enhancements here + def self.next_piece (board) + MyPiece.new(All_Pieces.sample, board) + end +end + +class MyBoard < Board + # your enhancements here + def next_piece + @current_block = MyPiece.next_piece(self) + @current_pos = nil + end +end + +class MyTetris < Tetris + # your enhancements here + + def initialize + @root = TetrisRoot.new + @timer = TetrisTimer.new + set_board + @running = true + key_bindings + buttons + run_game + end + + def set_board + @canvas = TetrisCanvas.new + @board = MyBoard.new(self) + @canvas.place(@board.block_size * @board.num_rows + 3, + @board.block_size * @board.num_columns + 6, 24, 80) + @board.draw + end + + def key_bindings + super + @root.bind('u', proc {@board.rotate_clockwise;@board.rotate_clockwise}) + end +end + + diff --git a/Programming Languages/Part C/Week 1/hw6graphics.rb b/Programming Languages/Part C/Week 1/hw6graphics.rb new file mode 100644 index 0000000..d768b37 --- /dev/null +++ b/Programming Languages/Part C/Week 1/hw6graphics.rb @@ -0,0 +1,111 @@ +# University of Washington, Programming Languages, Homework 6, hw6graphics.rb + +# This file provides an interface to a wrapped Tk library. The auto-grader will +# swap it out to use a different, non-Tk backend. + +require 'tk' + +class TetrisRoot + def initialize + @root = TkRoot.new('height' => 615, 'width' => 205, + 'background' => 'lightblue') {title "Tetris"} + end + + def bind(char, callback) + @root.bind(char, callback) + end + + # Necessary so we can unwrap before passing to Tk in some instances. + # Student code MUST NOT CALL THIS. + attr_reader :root +end + +class TetrisTimer + def initialize + @timer = TkTimer.new + end + + def stop + @timer.stop + end + + def start(delay, callback) + @timer.start(delay, callback) + end +end + +class TetrisCanvas + def initialize + @canvas = TkCanvas.new('background' => 'grey') + end + + def place(height, width, x, y) + @canvas.place('height' => height, 'width' => width, 'x' => x, 'y' => y) + end + + def unplace + @canvas.unplace + end + + def delete + @canvas.delete + end + + # Necessary so we can unwrap before passing to Tk in some instances. + # Student code MUST NOT CALL THIS. + attr_reader :canvas +end + +class TetrisLabel + def initialize(wrapped_root, &options) + unwrapped_root = wrapped_root.root + @label = TkLabel.new(unwrapped_root, &options) + end + + def place(height, width, x, y) + @label.place('height' => height, 'width' => width, 'x' => x, 'y' => y) + end + + def text(str) + @label.text(str) + end +end + +class TetrisButton + def initialize(label, color) + @button = TkButton.new do + text label + background color + command (proc {yield}) + end + end + + def place(height, width, x, y) + @button.place('height' => height, 'width' => width, 'x' => x, 'y' => y) + end +end + +class TetrisRect + def initialize(wrapped_canvas, a, b, c, d, color) + unwrapped_canvas = wrapped_canvas.canvas + @rect = TkcRectangle.new(unwrapped_canvas, a, b, c, d, + 'outline' => 'black', 'fill' => color) + end + + def remove + @rect.remove + end + + def move(dx, dy) + @rect.move(dx, dy) + end + +end + +def mainLoop + Tk.mainloop +end + +def exitProgram + Tk.exit +end diff --git a/Programming Languages/Part C/Week 1/hw6provided.rb b/Programming Languages/Part C/Week 1/hw6provided.rb new file mode 100644 index 0000000..7a82f9b --- /dev/null +++ b/Programming Languages/Part C/Week 1/hw6provided.rb @@ -0,0 +1,411 @@ +# University of Washington: Programming Languages +# Homework 6: A working Tetris game you will extend without breaking. + +require_relative './hw6graphics' + +# class responsible for the pieces and their movements +class Piece + + # creates a new Piece from the given point array, holding the board for + # determining if movement is possible for the piece, and gives the piece a + # color, rotation, and starting position. + def initialize (point_array, board) + @all_rotations = point_array + @rotation_index = (0..(@all_rotations.size-1)).to_a.sample + @color = All_Colors.sample + @base_position = [5, 0] # [column, row] + @board = board + @moved = true + end + + def current_rotation + @all_rotations[@rotation_index] + end + + def moved + @moved + end + + def position + @base_position + end + + def color + @color + end + + def drop_by_one + @moved = move(0, 1, 0) + end + + # takes the intended movement in x, y and rotation and checks to see if the + # movement is possible. If it is, makes this movement and returns true. + # Otherwise returns false. + def move (delta_x, delta_y, delta_rotation) + # Ensures that the rotation will always be a possible formation (as opposed + # to nil) by altering the intended rotation so that it stays + # within the bounds of the rotation array + moved = true + potential = @all_rotations[(@rotation_index + delta_rotation) % @all_rotations.size] + # for each individual block in the piece, checks if the intended move + # will put this block in an occupied space + potential.each{|posns| + if !(@board.empty_at([posns[0] + delta_x + @base_position[0], + posns[1] + delta_y + @base_position[1]])); + moved = false; + end + } + if moved + @base_position[0] += delta_x + @base_position[1] += delta_y + @rotation_index = (@rotation_index + delta_rotation) % @all_rotations.size + end + moved + end + + # class method to figures out the different rotations of the provided piece + def self.rotations (point_array) + rotate1 = point_array.map {|x,y| [-y,x]} + rotate2 = point_array.map {|x,y| [-x,-y]} + rotate3 = point_array.map {|x,y| [y,-x]} + [point_array, rotate1, rotate2, rotate3] + end + + # class method to choose the next piece + def self.next_piece (board) + Piece.new(All_Pieces.sample, board) + end + + # class array holding all the pieces and their rotations + All_Pieces = [[[[0, 0], [1, 0], [0, 1], [1, 1]]], # square (only needs one) + rotations([[0, 0], [-1, 0], [1, 0], [0, -1]]), # T + [[[0, 0], [-1, 0], [1, 0], [2, 0]], # long (only needs two) + [[0, 0], [0, -1], [0, 1], [0, 2]]], + rotations([[0, 0], [0, -1], [0, 1], [1, 1]]), # L + rotations([[0, 0], [0, -1], [0, 1], [-1, 1]]), # inverted L + rotations([[0, 0], [-1, 0], [0, -1], [1, -1]]), # S + rotations([[0, 0], [1, 0], [0, -1], [-1, -1]])] # Z + + # class array + All_Colors = ['DarkGreen', 'dark blue', 'dark red', 'gold2', 'Purple3', + 'OrangeRed2', 'LightSkyBlue'] +end + + +# Class responsible for the interaction between the pieces and the game itself +class Board + + def initialize (game) + @grid = Array.new(num_rows) {Array.new(num_columns)} + @current_block = Piece.next_piece(self) + @score = 0 + @game = game + @delay = 500 + end + + # both the length and the width of a block, since it is a square + def block_size + 15 + end + + def num_columns + 10 + end + + def num_rows + 27 + end + + # the current score + def score + @score + end + + # the current delay + def delay + @delay + end + + # the game is over when there is a piece extending into the second row + # from the top + def game_over? + @grid[1].any? + end + + # moves the current piece down by one, if this is not possible stores the + # current piece and replaces it with a new one. + def run + ran = @current_block.drop_by_one + if !ran + store_current + if !game_over? + next_piece + end + end + @game.update_score + draw + end + + # moves the current piece left if possible + def move_left + if !game_over? and @game.is_running? + @current_block.move(-1, 0, 0) + end + draw + end + + # moves the current piece right if possible + def move_right + if !game_over? and @game.is_running? + @current_block.move(1, 0, 0) + end + draw + end + + # rotates the current piece clockwise + def rotate_clockwise + if !game_over? and @game.is_running? + @current_block.move(0, 0, 1) + end + draw + end + + # rotates the current piece counterclockwise + def rotate_counter_clockwise + if !game_over? and @game.is_running? + @current_block.move(0, 0, -1) + end + draw + end + + # drops the piece to the lowest location in the currently occupied columns. + # Then replaces it with a new piece + # Change the score to reflect the distance dropped. + def drop_all_the_way + if @game.is_running? + ran = @current_block.drop_by_one + @current_pos.each{|block| block.remove} + while ran + @score += 1 + ran = @current_block.drop_by_one + end + draw + store_current + if !game_over? + next_piece + end + @game.update_score + draw + end + end + + # gets the next piece + def next_piece + @current_block = Piece.next_piece(self) + @current_pos = nil + end + + # gets the information from the current piece about where it is and uses this + # to store the piece on the board itself. Then calls remove_filled. + def store_current + locations = @current_block.current_rotation + displacement = @current_block.position + (0..3).each{|index| + current = locations[index]; + @grid[current[1]+displacement[1]][current[0]+displacement[0]] = + @current_pos[index] + } + remove_filled + @delay = [@delay - 2, 80].max + end + + # Takes a point and checks to see if it is in the bounds of the board and + # currently empty. + def empty_at (point) + if !(point[0] >= 0 and point[0] < num_columns) + return false + elsif point[1] < 1 + return true + elsif point[1] >= num_rows + return false + end + @grid[point[1]][point[0]] == nil + end + + # removes all filled rows and replaces them with empty ones, dropping all rows + # above them down each time a row is removed and increasing the score. + def remove_filled + (2..(@grid.size-1)).each{|num| row = @grid.slice(num); + # see if this row is full (has no nil) + if @grid[num].all? + # remove from canvas blocks in full row + (0..(num_columns-1)).each{|index| + @grid[num][index].remove; + @grid[num][index] = nil + } + # move down all rows above and move their blocks on the canvas + ((@grid.size - num + 1)..(@grid.size)).each{|num2| + @grid[@grid.size - num2].each{|rect| rect && rect.move(0, block_size)}; + @grid[@grid.size-num2+1] = Array.new(@grid[@grid.size - num2]) + } + # insert new blank row at top + @grid[0] = Array.new(num_columns); + # adjust score for full flow + @score += 10; + end} + self + end + + # current_pos holds the intermediate blocks of a piece before they are placed + # in the grid. If there were any before, they are sent to the piece drawing + # method to be removed and replaced with that of the new position + def draw + @current_pos = @game.draw_piece(@current_block, @current_pos) + end +end + +class Tetris + + # creates the window and starts the game + def initialize + @root = TetrisRoot.new + @timer = TetrisTimer.new + set_board + @running = true + key_bindings + buttons + run_game + end + + # creates a canvas and the board that interacts with it + def set_board + @canvas = TetrisCanvas.new + @board = Board.new(self) + @canvas.place(@board.block_size * @board.num_rows + 3, + @board.block_size * @board.num_columns + 6, 24, 80) + @board.draw + end + + def key_bindings + @root.bind('n', proc {self.new_game}) + + @root.bind('p', proc {self.pause}) + + @root.bind('q', proc {exitProgram}) + + @root.bind('a', proc {@board.move_left}) + @root.bind('Left', proc {@board.move_left}) + + @root.bind('d', proc {@board.move_right}) + @root.bind('Right', proc {@board.move_right}) + + @root.bind('s', proc {@board.rotate_clockwise}) + @root.bind('Down', proc {@board.rotate_clockwise}) + + @root.bind('w', proc {@board.rotate_counter_clockwise}) + @root.bind('Up', proc {@board.rotate_counter_clockwise}) + + @root.bind('space' , proc {@board.drop_all_the_way}) + end + + def buttons + pause = TetrisButton.new('pause', 'lightcoral'){self.pause} + pause.place(35, 50, 90, 7) + + new_game = TetrisButton.new('new game', 'lightcoral'){self.new_game} + new_game.place(35, 75, 15, 7) + + quit = TetrisButton.new('quit', 'lightcoral'){exitProgram} + quit.place(35, 50, 140, 7) + + move_left = TetrisButton.new('left', 'lightgreen'){@board.move_left} + move_left.place(35, 50, 27, 536) + + move_right = TetrisButton.new('right', 'lightgreen'){@board.move_right} + move_right.place(35, 50, 127, 536) + + rotate_clock = TetrisButton.new('^_)', 'lightgreen'){@board.rotate_clockwise} + rotate_clock.place(35, 50, 77, 501) + + rotate_counter = TetrisButton.new('(_^', 'lightgreen'){ + @board.rotate_counter_clockwise} + rotate_counter.place(35, 50, 77, 571) + + drop = TetrisButton.new('drop', 'lightgreen'){@board.drop_all_the_way} + drop.place(35, 50, 77, 536) + + label = TetrisLabel.new(@root) do + text 'Current Score: ' + background 'lightblue' + end + label.place(35, 100, 26, 45) + @score = TetrisLabel.new(@root) do + background 'lightblue' + end + @score.text(@board.score) + @score.place(35, 50, 126, 45) + end + + # starts the game over, replacing the old board and score + def new_game + @canvas.unplace + @canvas.delete + set_board + @score.text(@board.score) + @running = true + run_game + end + + # pauses the game or resumes it + def pause + if @running + @running = false + @timer.stop + else + @running = true + self.run_game + end + end + + # alters the displayed score to reflect what is currently stored in the board + def update_score + @score.text(@board.score) + end + + # repeatedly calls itself so that the process is fully automated. Checks if + # the game is over and if it isn't, calls the board's run method which moves + # a piece down and replaces it with a new one when the old one can't move any + # more + def run_game + if !@board.game_over? and @running + @timer.stop + @timer.start(@board.delay, (proc{@board.run; run_game})) + end + end + + # whether the game is running + def is_running? + @running + end + + # takes a piece and optionally the list of old TetrisRects corresponding + # to it and returns a new set of TetrisRects which are how the piece is + # visible to the user. + def draw_piece (piece, old=nil) + if old != nil and piece.moved + old.each{|block| block.remove} + end + size = @board.block_size + blocks = piece.current_rotation + start = piece.position + blocks.map{|block| + TetrisRect.new(@canvas, start[0]*size + block[0]*size + 3, + start[1]*size + block[1]*size, + start[0]*size + size + block[0]*size + 3, + start[1]*size + size + block[1]*size, + piece.color)} + end +end + +# To help each game of Tetris be unique. +srand diff --git a/Programming Languages/Part C/Week 1/hw6runner.rb b/Programming Languages/Part C/Week 1/hw6runner.rb new file mode 100644 index 0000000..6261d8a --- /dev/null +++ b/Programming Languages/Part C/Week 1/hw6runner.rb @@ -0,0 +1,27 @@ +# University of Washington, Programming Languages, Homework 6, hw6runner.rb + +require_relative './hw6provided.rb' +require_relative './hw6assignment.rb' + +def runTetris + Tetris.new + mainLoop +end + +def runMyTetris + MyTetris.new + mainLoop +end + +if ARGV.count == 0 + runMyTetris +elsif ARGV.count != 1 + puts "usage: hw6runner.rb [enhanced | original]" +elsif ARGV[0] == "enhanced" + runMyTetris +elsif ARGV[0] == "original" + runTetris +else + puts "usage: hw6runner.rb [enhanced | original]" +end + diff --git a/Programming Languages/Part C/Week 1/test.rb b/Programming Languages/Part C/Week 1/test.rb index e69de29..cc4f954 100644 --- a/Programming Languages/Part C/Week 1/test.rb +++ b/Programming Languages/Part C/Week 1/test.rb @@ -0,0 +1 @@ +puts "Hello World" \ No newline at end of file