ICM Final
UPDATE: FINAL CODE HAS BEEN ADDED TO THE BOTTOM OF THE PAGE
What Does It Take To Be A Major League Baseball Player?
I don’t know if a lot of you are aware but it is very, very difficult to be a Major League Baseball Player. In most professional sports, there are few steps: you play in college, you get drafted, and if you prove good enough you get to play professional football, hockey or basketball. This is not the case in Major League Baseball. If you are 100% the best player in college, if there has never been anyone like you to play the game at the rare age of 18 or 21 you are still placed in a brutal minor league system that can completely control your soul. Let’s take a look at a trailer for the film “A Player to be Named Later” for more information.
I asked this question to give you a better idea of why I love this sport so much: it’s not just a sport it’s a story that grows and changes every single year and never gets boring. There are truly millions of examples of this but let’s stick with one that happened this past year. For those who don’t know, pitchers rarely get the chance to hit. A typical batter will get upwards of 600 – 700 opportunities to get a hit and a pitcher may get like…50 at most. Michael Lorenzon, a pitcher for the Reds this year – got five. Five opportunities to just get a hit. On around August 12th, Michael Lorenzon’s father – whom he was very close with – passed away. After returning from a bereavement list, Lorenzon was put into the game for his team.
This is why I love baseball.
As is true with all things you love, you want to know more about it and maybe try to instill some of that information in others.
When I started learning coding awhile ago, I was a bit terrified because I was in an unfamiliar place. However, when I started using baseball statistics in coding, the experience of discomfort lessened. Coding became secondary, a byproduct of needing to learn how to make baseball more accessible. I started with a simple baseball game:
When I made this, I was super excited. I had finally made a baseball game! Very strange to look back on it now. The evolution of the game is entirely thanks to the introduction of JSON files. They gave me the opportunity to take real baseball data – of which there is an abundance – and apply it to my work. I’ll never forget being late to a bar that night because I was so excited to make this:
After I had fun with this, I decided why not mix the two, which led me to my final. How cool would it be if I could take real data and use it to show the difference in a pitchers arsenal. I’ve seen plenty of videos and .gif’s but something about visualizing the data got me really excited. Luckily, I found two amazing partners – and fellow baseball fans – in Katie Takacs and Grant Henry and after countless hours we made what can only best be shown in a local host.
Here is the final code:
var gameMode = ‘EASY’;
//EASY MODE
var x = 380;
var y = 285;
var speed = 3.5;
var speedb = 0;
var accx = -0.01;
var deg = 0;
var serial;
var serialTwo;
var serialThree;
var ballX, ballY, ballZ;
var batX, batY, batZ;
var portName = ‘/dev/cu.AdafruitEZ-Link71f3-SPP’; ///dev/cu.AdafruitEZ-Link71f3-SPP; for accelerometer
var portNameTwo = ‘/dev/cu.AdafruitEZ-Link743a-SPP’; ///dev/cu.AdafruitEZ-Link743a-SPP; for buttons
var portNameThree = ‘/dev/cu.usbmodem1421′;//’/dev/cu.usbmodem1411’; or whatever port you’re plugged into for FSR’s
var w = 35;
var prevBallY;
var counter = 150;
var batspeed = 5;
//hits counter variables
var score = 0;
var imgScore;
var bringEm;
var shaggy;
var noMatter;
var swinging = false;
//var getHit = false;
var inData;
var outByte = 0;
//HARD MODE
var sensorValue = 5;
var ball, pitchSelector, data, data2;
var bigList = [];
var speedHard = 3;
//p is from 0-4 and determines which pitcher’s stat’s we’re accessing: 0 is Thor, 1 is kershaw, etc
var p = 1;
//k is however many pitches the pitcher has in their arsenal
var k = 4;
// r is the minimumim vel for each pitcher
var r = 30;
//b is second real value in PitchMapMin
var b = 101.4;
//s is first real value in PitchMapMov
var s = -9.0;
//q is second real value in PitchMapMov
var q = 5;
// changes colors in the switch to make colors unanimous across the board
var cOne = 255;
var cTwo = 255;
var cThree = 0;
var cFour = 244;
var cFive = 66;
var cSix = 66;
//pitchers display variables
var currentImg;
var kershaw;
var mo;
var thor;
var wake;
var yu;
//Wake’s variables. Adj makes his movement start earlier, Knuckle allows for his wacky movement
// to be in the code
var wakeAdj = 400;
var knuckle = 0;
// making JSON data global.
var PitchMax;
var PitchMin;
var PitchMapMov;
var PitchMapMax;
var PitchMapMin;
var PitchMapMov;
var PitchrandomSpeed;
var PitchSpeed;
//GAME MODE
var speedc = 1;
// timer variables
var clockStart = 60;
var interval;
var modeEasy = false;
var modeHard = false;
var modeGame = false;
var modeMenu = false;
function preload() {
img = loadImage(‘backgroundField.png’);
image2 = loadImage(‘Bat.png’);
image3 = loadImage(‘baseball.png’);
image4 = loadImage(‘mainMenu3.png’);
mySound = loadSound(‘Cheer.mp3’);
// load pitchers images
kershaw = loadImage(‘kershawTag2.png’);
mo = loadImage(‘moTag.png’);
thor = loadImage(‘thorTag.png’);
wake = loadImage(‘wakeTag.png’);
yu = loadImage(‘yuTag.png’);
// load easter egg sounds
bringEm = loadSound(‘BringEmOut.wav’);
shaggy = loadSound(‘Boombastic.wav’);
noMatter = loadSound(‘WinNoMatter.wav’);
//load pitcher data
data = loadJSON(‘ReorderedPitcherData.json’, gotData);
data2 = loadJSON(‘ReorderedPitcherData2.json’, gotData2);
}
function setup() {
createCanvas(800, 650);
angleMode(DEGREES);
startTimer();
noMatter.onended(startTimer);
//starts at Kershaw
currentImg = kershaw;
//sensorValue = 1;
serial = new p5.SerialPort(“localhost”, 8083);
serial.on(‘list’, printList);
serial.on(‘data’, serialEvent);
serial.on(‘open’, openPort);
serial.list();
serial.open(portName);
serialTwo = new p5.SerialPort(“localhost”, 8081);
serialTwo.on(‘list’, printList);
serialTwo.on(‘data’, serialEventTwo);
serialTwo.on(‘open’, openPortTwo);
serialTwo.open(portNameTwo);
serialThree = new p5.SerialPort(“localhost”, 8082);
serialThree.on(‘list’, printList);
serialThree.on(‘data’, serialEventThree);
serialThree.on(‘open’, openPortThree);
serialThree.open(portNameThree);
textSize(24);
}
function draw() {
clear();
image(img, -30, -50, width+30, height);
// batMove();
// if(getHit === true){
// //console.log(“buzz?”);
// serial.write(“C”);
// console.log(‘writing c’)
// //console.log(serial.write);
// }
// if(getHit === false){
// serial.write(“L”);
// console.log(‘writing L’)
// }
// background(0);
// fill(255);
// text(batX + ” ” + batY + ” ” + batZ, width/2, height/2);
if (ballX === 0 && ballY === 0 && ballZ === 0 && clockStart === 60){
modeMenu = true;
modeEasy = false;
modeHard = false;
modeGame = false;
}
if(modeMenu === true){
image(image4, 0, 0, width+40, height);
modeEasy = false;
modeHard = false;
modeGame = false;
}
if (ballX > 400){
clear();
modeEasy = true;
modeMenu = false;
modeHard = false;
modeGame = false;
clockStart = 59;
score = 0;
}
if(modeEasy === true) {
//console.log(“why aren’t you swinging”);
printTimerShmimer();
printScore();
baseball();
pitch();
returnball();
bat();
contact();
}
if (ballY > 400){
clear();
modeEasy = false;
modeHard = true;
modeMenu = false;
modeGame = false;
clockStart = 59;
score = 0;
}
if(modeHard === true){
//what causes the ball itself to move
if(typeof ball != “undefined”){
ball.display();
ball.move();
if(ball.inScreen()) {
for (var i = 0; i < data.pitchers[p].pitches.length ; i++){
for (var j = 0; j < data.pitchers[p].pitches[i][“Frequency”]/10; j++){
bigList.push(i);
}
}
pitchSelector = bigList[floor(random(bigList.length))];
//console.log(“pitch selection” + pitchSelector);
ball = new Thor(pitchSelector);
//console.log(“Sensor Value: ” + sensorValue);
}
// pitchDisplay();
}
printScore();
printTimerShmimer();
bat();
contactHard();
// changePitcher();
}
if (ballZ > 400){
clear();
modeHard = false;
modeGame = true;
modeMenu = false;
modeEasy = false;
clockStart = 59;
score = 0;
}
if(modeGame === true){
//pitcher image display
if (typeof currentImg != “undefined”) {
image(currentImg, 80, 5, 125, 250);
}
//what causes the ball itself to move
if(typeof ball != “undefined”){
ball.display();
ball.move();
barDisplay();
if(ball.inScreen()) {
for (var i = 0; i < data2.pitchers[p].pitches.length ; i++){
for (var j = 0; j < data2.pitchers[p].pitches[i][“Frequency”]/10; j++){
bigList.push(i);
}
}
pitchSelector = bigList[floor(random(bigList.length))];
//console.log(“pitch selection” + pitchSelector);
ball = new Thor2(pitchSelector);
//console.log(“Sensor Value: ” + sensorValue);
}
pitchDisplay();
}
//console.log(PitchrandomSpeed);
pitchText();
pitchDisplay();
printScore();
printTimerShmimer();
bat();
contactGame();
changePitcher();
adjustStats();
}
}
//baseball display
function baseball() {
fill(255);
ellipseMode(CORNER);
ellipse(x, y, w, w);
image(image3, x, y, w-1, w-1);
}
//baseball movement
function pitch() {
y = y + speed;
x = x + speedb;
// speedb = speedb + accx * 2;
if (y >= height*2.5) {
y = 235;
}
}
//baseball restarts
function returnball() {
if (x > 800 || x < -100 || y > height*1.65 || y < -900) {
baseball();
x = 380;
y = 285;
speed = 3;
speedb = 0;
}
}
//batter up
function bat() {
translate(245, 450);
rotate(deg);
noStroke();
fill(100, 100, 100, 0);
image(image2, 0, 0, 25, 180);
// console.log(“bat”);
// console.log(swinging);
if (swinging) {
deg = deg – batspeed;
}
if (deg <= -180) {
deg = 0;
swinging = false;
}
var diffY = ballY – prevBallY;
if (batX > 105 && batX < 120 && counter > 150) {
swinging = true;
counter = 0 ;
}
// console.log(deg);
counter++;
prevBallY = ballY;
if (y < 225 || x < 0) {
serial.write(“L”);
}
}
//bat hits ball
function contact() {
var diff = deg + 90;
image(image2, 0, 0, 25, 180);
if (abs(diff) < 10) {
if (y > 442 && y < 558) {
if (!mySound.isPlaying()) {
mySound.play();
serial.write(“C”);
speed = speed * random(-4, -1);
speedb = speedb + random(-10, 5);
// see addScore function…
addScore();
}
}
}
}
//}
// send when an event occurs *GAME MODE*
function Thor2(pitch/*frequency*/) {
this.x = 375;
this.y = 220;
this.speedHard = 1;
this.speedc = 0;
this.w = 10;
this.rgb = [0,200,100];
// noprotect
switch (pitch){
//knuckleball will be
case 0:
//red…fastball
this.rgb = [cFour,cFive,cSix];
//console.log(this.rgb);
break;
case 1:
//yellow… curveball
this.rgb = [cOne,cTwo,cThree];
//console.log(this.rgb);
break;
case 2:
//turqouise … change-up
this.rgb = [66,244,235];
//console.log(this.rgb);
break;
case 3:
//purple… slider
this.rgb = [125,66,244];
//console.log(this.rgb);
break;
case 4:
//lime green…sinker
this.rgb = [0,255,0];
//console.log(this.rgb);
break;
case 5:
// blue …cutter
this.rgb = [0, 0, 205];
//console.log(this.rgb);
break;
case 6:
//orange…two-seamer
this.rgb = [255,165, 0];
//console.log(this.rgb);
break;
default:
this.rgb = [0,200,100];
//console.log(‘no color was passed into ball’);
}
PitchMax = data2.pitchers[p].pitches[pitch].MaxVelo;
PitchMin = data2.pitchers[p].pitches[pitch].MinVelo;
PitchxMov = data2.pitchers[p].pitches[pitch].xMov;
//mapping of maximum velocity
PitchMapMax = map(PitchMax, 50, b, 0, 8);
//mapping of minimum velocity
PitchMapMin = map(PitchMin, r, b, 0, 8);
// mapped xMov of fastball
PitchMapMov = map(PitchxMov, s, q, -7, 4);
// pitch velo will randomly vary between max and min
PitchrandomSpeed = random(PitchMapMax, PitchMapMin);
PitchSpeed = Math.round(random(PitchMin, PitchMax));
this.display = function(){
fill(this.rgb[0],this.rgb[1],this.rgb[2], 100);
image(image3, this.x, this.y, this.w, this.w);
ellipseMode(CORNER);
ellipse(this.x, this.y, this.w-1, this.w-1);
}
this.move = function(){
this.y = this.y + this.speedHard + PitchrandomSpeed;
this.x = this.x + this.speedc;
if(this.y> wakeAdj) {
this.x = this.x + PitchMapMov + knuckle;
}
if (this.y > 400){
this.w = this.w +2;
}
if (this.y < 219) {
this.w = this.w – 1.65;
}
};
this.inScreen = function(){
if (this.y < 225 || this.x < 0) {
serial.write(“L”);
}
if(this.y > height*2 || this.y < -500) {
return true;
}
};
}
// send when an event occurs *HARD*
function Thor(pitch/*frequency*/) {
this.x = 375;
this.y = 220;
this.speedHard = 3.5;
this.speedb = 0;
this.w = 10;
this.rgb = [0,200,100];
// noprotect
switch (pitch){
//knuckleball will be
case 0:
//red…fastball
this.rgb = [cFour,cFive,cSix];
//console.log(this.rgb);
break;
case 1:
//yellow… curveball
this.rgb = [cOne,cTwo,cThree];
//console.log(this.rgb);
break;
case 2:
//turqouise … change-up
this.rgb = [66,244,235];
//console.log(this.rgb);
break;
case 3:
//purple… slider
this.rgb = [125,66,244];
//console.log(this.rgb);
break;
case 4:
//lime green…sinker
this.rgb = [0,255,0];
// console.log(this.rgb);
break;
case 5:
// blue …cutter
this.rgb = [0, 0, 205];
// console.log(this.rgb);
break;
case 6:
//orange…two-seamer
this.rgb = [255,165, 0];
//console.log(this.rgb);
break;
default:
this.rgb = [0,200,100];
// console.log(‘no color was passed into ball’);
}
PitchMax = data.pitchers[p].pitches[pitch].MaxVelo;
PitchMin = data.pitchers[p].pitches[pitch].MinVelo;
PitchxMov = data.pitchers[p].pitches[pitch].xMov;
//mapping of maximum velocity
PitchMapMax = map(PitchMax, 50, b, 0, 8);
//mapping of minimum velocity
PitchMapMin = map(PitchMin, r, b, 0, 8);
// mapped xMov of fastball
PitchMapMov = map(PitchxMov, s, q, -7, 4);
// pitch velo will randomly vary between max and min
PitchrandomSpeed = random(PitchMapMax, PitchMapMin);
PitchSpeed = Math.round(random(PitchMin, PitchMax));
this.display = function(){
fill(this.rgb[0],this.rgb[1],this.rgb[2], 100);
image(image3, this.x, this.y, this.w, this.w);
ellipseMode(CORNER);
ellipse(this.x, this.y, this.w-1, this.w-1);
}
this.move = function(){
this.y = this.y + this.speedHard;
this.x = this.x + this.speedb;
if(this.y> wakeAdj) {
this.x = this.x + PitchMapMov + knuckle;
}
if (this.y > 400){
this.w = this.w +2;
}
if (this.y < 219) {
this.w = this.w – 1.70;
serial.write(“L”);
}
};
this.inScreen = function(){
if (this.y < 225 || this.x < 0) {
serial.write(“L”);
}
if(this.y > height*2 || this.y < -500) {
return true;
}
};
}
function gotData(){
ball = new Thor(0);
}
function gotData2(){
ball = new Thor2(0);
}
// bat hits ball
function contactHard() {
var diff = deg + 90;
image(image2, 0, 0, 25, 180);
if (abs(diff) < 10) {
if (ball.y > 442 && ball.y < 558 && ball.x > 325 && ball.x < 450) {
if (!mySound.isPlaying()) {
serial.write(“C”);
mySound.play();
ball.speedHard = ball.speedHard – 15;
ball.speedb = ball.speedb + random(-10,5);
// see addScore function…
addScore();
}
}
}
}
// bat hits ball
function contactGame() {
var diff = deg + 90;
image(image2, 0, 0, 25, 180);
if (abs(diff) < 10) {
if (ball.y > 442 && ball.y < 558 && ball.x > 325 && ball.x < 450) {
if (!mySound.isPlaying()) {
serial.write(“C”);
mySound.play();
ball.speedHard = ball.speedHard – 15;
ball.speedc = ball.speedc + random(-10,5);
// see addScore function…
addScore();
}
}
}
}
//score counter
function addScore() {
//add one
score++;
//Easter egg sounds
if (score == 5) {
bringEm.play();
}
if (score == 10) {
shaggy.play();
}
}
//scroll through the pitchers when right arrow is pressed
function changePitcher() {
if (sensorValue == 1) {
if (currentImg == kershaw) {
currentImg = thor;
}
}
if (sensorValue == 2) {
if(currentImg == thor) {
currentImg = yu;
}
}
if (sensorValue == 3) {
if(currentImg == yu) {
currentImg = mo;
}
}
if (sensorValue == 4) {
if (currentImg == mo) {
currentImg = wake;
}
}
if (sensorValue == 5) {
if (currentImg == wake) {
currentImg = kershaw;
}
}
}
function adjustStats(){
if(sensorValue == 1){
thorSpecifics();
}
if(sensorValue == 2){
yuSpecifics();
}
if(sensorValue == 3){
moSpecifics();
}
if (sensorValue == 4) {
wakeSpecifics();
}
if(sensorValue == 5){
kershawSpecifics();
}
}
//Displays the score
function printScore() {
textAlign(LEFT);
fill(255, 200);
textSize(40);
textStyle(BOLD);
// image(imgScore, 300, 15, 220, 150);
text(“HITS ” + score, width-350, 185);
if (score == 5){
fill(0, 200);
// textSize(40);
text(“HITS ” + score, width-350, 185);
textAlign(CENTER);
text( “HOMERUN!”, width/2, 50);
}
if (score == 10) {
fill(0, 200);
// textSize(40);
text(“HITS ” + score, width-350, 185);
textAlign(CENTER);
text( “Grant Slam!”, width/2, 50);
}
if (score == 15){
fill(0, 200);
// textSize(40);
text(“HITS ” + score, width-350, 185);
textAlign(CENTER);
text( “Fasten Your Seatbelts!”, width/2, 50);
}
if (score == 20){
fill(0, 200);
// textSize(40);
text(“HITS ” + score, width-350, 185);
textAlign(CENTER);
textSize(20);
text( “Wicked Clutch Hittah!”, width/2, 50);
}
}
function barDisplay(){
noStroke();
fill(90, 200);
rect(25, 545, 190, 40);
}
function pitchText(){
textStyle(BOLD);
textAlign(CENTER);
textSize(10);
text(“PITCH | SPEED”, 120, 540);
}
function textDisplay(){
strokeWeight(2);
stroke(50);
textSize(20);
textStyle(BOLD);
textAlign(CENTER);
}
function printList(portList) {
for (var i = 0; i < portList.length; i++) {
// println(i + ” ” + portList[i]);
}
}
function openPort() {
serial.write(‘x’);
console.log(“port 1”);
}
function openPortTwo() {
serialTwo.write(‘x’);
console.log(“port 2”);
}
function openPortThree() {
serialThree.write(‘x’);
console.log(“port 3”);
}
//accelerometer/vibrating sensor
function serialEvent() {
var inString = serial.readStringUntil(‘\r\n’);
if (inString.length > 0) {
if (inString !== ‘hello’) {
var values = split(inString, ” “);
if (values.length > 0) {
batX = Number(values[0]);
// batY = Number(values[1]);
// batZ = Number(values[2]);
}
//console.log(batX);
}
serial.write(‘x’);
}
}
// button
function serialEventTwo() {
var inStringTwo = serialTwo.readLine();
if (inStringTwo.length > 0) {
inStringTwo = inStringTwo.trim();
sensorValue = inStringTwo;
//console.log(sensorValue);
//sensorValue = Number(inString/4);
}
}
//FSR’s
function serialEventThree(){
var inStringThree = serialThree.readStringUntil(‘\r\n’);
//console.log(inStringThree);
if (inStringThree.length > 0) {
if(inStringThree !== ‘hello’) {
var valuesFSR = split(inStringThree, ” “);
if (valuesFSR.length > 2) {
ballX = Number(valuesFSR[0]);
ballY = Number(valuesFSR[1]);
ballZ = Number(valuesFSR[2]);
//console.log(ballX);
}
}
serialThree.write(‘x’);
}
}
function thorSpecifics(){
p = 0;
k = 5;
r = 50;
s = -9.0;
q = 5.0;
b = 101.4;
cOne = 255;
cTwo = 255;
cThree = 0;
cFour = 244;
cFive = 66;
cSix = 66;
bigList = [];
wakeAdj = 400;
knuckle = 0;
pitchMapMov = map(PitchxMov, s, q, -7, 4);
}
function yuSpecifics(){
p = 2;
k = 7;
r = 50;
s = -10.0;
q = 10.0;
b = 101.4
cOne = 255;
cTwo = 255;
cThree = 0;
cFour = 244;
cFive = 66;
cSix = 66;
bigList = [];
wakeAdj = 400;
knuckle = 0;
pitchMapMov = map(PitchxMov, s, q, -7, 4);
}
function moSpecifics (){
p = 3;
k = 1;
r = 50;
s = -10.0;
q = 5.0;
b = 101.4;
cOne = 0;
cTwo = 0;
cThree = 205;
cFour = 244;
cFive = 66;
cSix = 66;
bigList = [];
wakeAdj = 400;
knuckle = 0;
pitchMapMov = map(PitchxMov, s, q, -7, 4);
}
function wakeSpecifics(){
p = 4;
k = 1;
r = 40;
s = -9;
q = 5;
b = 77.0;
cOne = 255;
cTwo = 255;
cThree = 0;
cFour = 255;
cFive = 105;
cSix = 180;
wakeAdj = 250;
pitchMapMov = 0;
knuckle = random(-10, 10);
bigList = [];
}
function kershawSpecifics(){
p = 1;
k = 4;
r = 30;
s = -9.0;
q = 5.0;
b = 101.4;
cOne = 255;
cTwo = 255;
cThree = 0;
cFour = 244;
cFive = 66;
cSix = 66;
wakeAdj = 400;
knuckle = 0;
pitchMapMov = map(PitchxMov, s, q, -7, 4);
bigList = [];
}
function pitchDisplay(){
//kershaw
if(currentImg == kershaw && sensorValue == 5 && pitchSelector == 0 && ball.y > height){
textDisplay();
//red
fill(255, 102, 102);
text(“Fastball: ” + PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == kershaw && sensorValue == 5 && pitchSelector == 1 && ball.y > height){
textDisplay();
//yellow
fill(255, 255, 0);
text(“Curveball: ” + PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == kershaw && sensorValue == 5 && pitchSelector == 2 && ball.y > height){
textDisplay();
//cyan
fill(66, 244, 235);
text(“Change-Up: ” + PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == kershaw && sensorValue == 5 && pitchSelector == 3 && ball.y > height){
textDisplay();
//purple
fill(236, 179, 255);
text(“Slider: ” + PitchSpeed + ” MPH”, 120, 570);
}
//Thor
if(currentImg == thor && sensorValue == 1 && pitchSelector == 0 && ball.y > height){
textDisplay();
//red
fill(255, 102, 102);
text(“Fastball: ” + PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == thor && sensorValue == 1 && pitchSelector == 1 && ball.y > height){
textDisplay();
//yellow
fill(255, 255, 0);
text(“Curveball: ” + PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == thor && sensorValue == 1 && pitchSelector == 2 && ball.y > height){
textDisplay();
//cyan
fill(66, 244, 235);
text(“Change-Up: ” + PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == thor && sensorValue == 1 && pitchSelector == 3 && ball.y > height){
textDisplay();
//purple
fill(236, 179, 255);
text(“Slider: ” + PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == thor && sensorValue == 1 && pitchSelector == 4 && ball.y > height){
textDisplay();
//green
fill(0, 255, 0);
text(“Sinker: ” + PitchSpeed + ” MPH”, 120, 570);
}
// Darvish
if(currentImg == yu && sensorValue == 2 && pitchSelector == 0 && ball.y> height){
textDisplay();
//red
fill(255, 102, 102);
text(“Fastball: “+ PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == yu && sensorValue == 2 && pitchSelector == 1 && ball.y>height){
textDisplay();
//yellow
fill(255, 255, 0);
text(“Curveball: ” + PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == yu && sensorValue == 2 && pitchSelector == 2 && ball.y>height){
textDisplay();
//cyan
fill(66, 244, 235);
text(“Change-Up: “+ PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == yu && sensorValue == 2 && pitchSelector == 3 && ball.y>height){
textDisplay();
//purple
fill(236, 179, 255);
text(“Slider: “+ PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == yu && sensorValue == 2 && pitchSelector == 4 && ball.y>height){
textDisplay();
//green
fill(0, 255, 0);
text(“Sinker: ” + PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == yu && sensorValue == 2 && pitchSelector == 5 && ball.y>height){
textDisplay();
//blue
fill(102, 163, 255);
text(“Cutter: “+ PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == yu && sensorValue == 2 && pitchSelector == 6 && ball.y>height){
textDisplay();
//orange
fill(255, 165, 0);
text(“Two-Seam: “+ PitchSpeed + ” MPH”, 120, 570);
}
//Rivera
if(currentImg == mo && sensorValue == 3 && pitchSelector == 0 && ball.y>height){
textDisplay();
//red
fill(255, 102, 102);
text(“Fastball: “+ PitchSpeed + ” MPH”, 120, 570);
}
if(currentImg == mo && sensorValue == 3 && pitchSelector == 1 && ball.y>height){
textDisplay();
//blue
fill(102, 163, 255);
text(“Cutter: “+ PitchSpeed + ” MPH”, 120, 570);
}
//Wakefield
if(currentImg == wake && sensorValue == 4 && pitchSelector == 0 && ball.y>height){
textDisplay();
//pink
fill(255, 105, 180);
text(“Knuckleball: “+ PitchSpeed + ” MPH”, 120, 570);
}
}
//Timer Interval Set – run function every 1000millis
function startTimer() {
clockStart = 60;
//console.log(‘timer starting’);
interval = setInterval(timerShmimer, 1000);
}
//Timer subtract and stop when you get to 0
function timerShmimer() {
clockStart–;
if (clockStart === 0) {
clockStart = 0;
clearInterval(interval);
noMatter.play();
}
}
//display for Timer
function printTimerShmimer() {
textSize(40);
fill(255, 200);
textAlign(LEFT);
text(” TIME ” + clockStart, width-400, height-520);
// text(clockStart, width-200, height-525);
}
//SWITCH BETWEEN THE MODES**PITCHERS IN GAME MODE
function keyPressed() {
if (keyCode == UP_ARROW) {
swinging = true;
} else if (keyCode == RIGHT_ARROW) {
gameMode = ‘EASY’;
} else if (keyCode == LEFT_ARROW){
gameMode = ‘HARD’;
} else if (keyCode == DOWN_ARROW){
gameMode = ‘GAME’;
}
if (keyCode == ENTER) {
if (currentImg == kershaw) {
sensorValue = 1;
currentImg = thor;
} else if (currentImg == thor) {
sensorValue = 2;
currentImg = yu;
} else if (currentImg == yu) {
sensorValue = 4;
currentImg = wake;
} else if (currentImg == wake) {
sensorValue = 3;
currentImg = mo;
} else if (currentImg == mo) {
sensorValue =5;
currentImg = kershaw;
}
}
return false;
}
//swings the bat upon click
function mousePressed() {
// console.log(“mouse pressed”);
swinging = true;
}
function keyPressed() {
if (keyCode == UP_ARROW) {
gameMode = ‘MENU’;
} else if (keyCode == RIGHT_ARROW) {
gameMode = ‘EASY’;
} else if (keyCode == LEFT_ARROW){
gameMode = ‘HARD’;
} else if (keyCode == DOWN_ARROW){
gameMode = ‘GAME’;
}
if (keyCode == ENTER) {
if (currentImg == kershaw) {
sensorValue = 1;
currentImg = thor;
} else if (currentImg == thor) {
sensorValue = 2;
currentImg = yu;
} else if (currentImg == yu) {
sensorValue = 4;
currentImg = wake;
} else if (currentImg == wake) {
sensorValue = 3;
currentImg = mo;
} else if (currentImg == mo) {
sensorValue =5;
currentImg = kershaw;
}
}
return false;
}
Virtual Batting Cage Updates
We’ve made a lot of progress in the past couple weeks on our project, mostly in terms of Fabrication and computing. We’ve constructed a helmet equipped with two buttons – one to toggle between pitchers and one to take you to the home screen. The purpose of the pitcher button – located on the brim of the helmet – is to simulate what a hitter may do physically while at the plate. While the .gif below is meant as a comedic exaggeration, it’s not uncommon for a batter to touch the brim of his helmet before taking a pitch; there may be some valuable tar there to make his hands stick to his bat better.
We’ve also constructed a place for the batter to stand when they are taking their ‘at-bat’. The next step for this will be to integrate FSR’s into the home plate. We would also like to signal where the batter should stand.
In terms of the coding, we are making some real great strides as well. Here is a sketch showing what the final background will look like:
And here is a video showing what the final code will look like in terms of pitchers.
Finally there is a preview of what this will look like when this is all tied together.
The Virtual Batting Cage
“Baseball is dull only to dull minds”
I am a baseball fan. Have been since I was a little kid and will likely be till the day I die. To me the sport transcends the physical space it is played on and enters into a narrative and symbolic realm. When you tend to be vocal about baseball like I am, you hear a lot of rebukes: “How hard could it be?” “Why are the players so fat?” “It’s so boring.” Well, to quote the great Red Barber, “Baseball is dull only to dull minds” and I guess I intend to do some sharpening. My ICM finale will attempt to educate users about baseball by having them play the sport themselves. I hope to show them the nuance, the beauty and the difficulty of the sport both visually and physically.
I know a good amount about baseball, which is to say I know about…oh, let’s say 30% of what baseball has to teach. One area that I struggle with is pitch recognition. For those unfamiliar, a pitcher has a certain arsenal with which he throws i.e. a fastball, a changeup, a curveball and a slider. If I were to describe these pitches to you they would sound very different but when they are moving at 90+ MPH, they’re hard to tell the difference. How can I create something that helps pitch recognition but isn’t just me sitting down at a screen and watching baseball highlights?
“Baseball is ninety percent mental and the other half is physical”
Over the summer I started working for a baseball website that does exactly what I am looking to do. I guess I was sort of curious as to how I could make this visual experience a more interactive one. For my PComp Midterm, I took the following sketch and replaced the mouse-pressed bat with an actual physical bat but this was just the beginning. What I aim to do with my ICM final is to make this a full-fledged experience. There will be a home plate that is essentially a trigger for three different modes. If you tap on the green part of the plate, the p5 sketch will throw fastballs right down the middle; this will be “Easy Mode”. If you tap on the yellow part of the plate, there will be a “Hard Mode” that features more challenging pitches like curveballs and sliders. If you press the red part “Game Mode”, an image will appear in the top left hand corner of the sketch featuring a pitcher who you will face off against.
Due to the fact that this is both a PComp and an ICM final, I’ve spent a bit more time focusing on the physical elements first as I feel that it will dictate what the ICM final ends up looking like. Truth be told though, it wasn’t until I play test the PComp idea that I got a lot of great feedback on what the ICM sketch should incorporate. Below is a tentative sketch as to what the screen will look during “Game Mode”.
As you can see, there’s a mound and a ball being thrown towards you. There’s a batter’s box and a home plate, which will resemble what the user is actually standing on: a grass batters box next to a home plate. In the top left hand corner will be the face of the pitcher you are facing off against. Below him will be his arsenal. In the center of the screen there will be a scoreboard keeping track of how much time you have left to accrue hits and a hit counter.
Code
Luckily, a big part of the actual code is done due to my midterm, but there is still a lot left to accomplish. I need to create my own JSON file with various pieces of data from the pitchers we want to incorporate into the sketch. I need to figure out a way to map that data so that it appears accurately and realistically into the sketch too. After that, we can work on the other, more aesthetic features. Work has already begun on creating a button that can switch between different pitchers and is featured below (code here).
More?
As of now, there are still a lot of things that can be added and subtracted from the experience. I’m interested in providing as much information as possible while maintaining clarity in the sketch. The last thing I would want would be for the user to be overloaded with information. I would like to provide just enough for them to develop a genuine curiosity about baseball so that they might pursue an interest in it in the future.
The Future
Ultimately, I would love to take this an put it into an even more immersive 3D experience. I feel like you’ll be able to get a good understanding of pitch recognition on a 2D plain, but you won’t be able to fully get it until you experience it in 3D.
Update!
Since last week, there have been a lot of strides made, here are a few of them:
Frequency.
The particular sketch that you are looking at is not only representative of the arsenal that Noah “Thor” Syndergaard threw in the 2016 season but also represents how frequently he threw said pitch:
Stadium and Scoreboard
This sketch shows a close resemblance to what the final product will look like. The pitchers will be able to be rotated (here it is using the keypressed function but in the final you will press a button located on the helmet (see video)), and a scoreboard will tally how many hits you’ve gathered.
Button
A very cranky girlfriend uses the button function
BABIP Comparisons
The below is an example and is much better viewed in full screen
So for this assignment I knew that I wanted to do something with baseball data. Luckily for me, baseball data is plentiful and easy to access; my go to is Fangraphs.
It’s easy to visualize baseball data but difficult to do so in a way that can be revealing. Anyone can great a histogram of all the HR’s that have been hit in June or by left handed batters, but that data visualization doesn’t really serve to reveal anything. I was curious in how the data can inform me of something that I didn’t know before.
In the past ten years or so, or definitely since the release of the novel and movie Moneyball, the baseball world has been obsessed with sabermetrics which is the application of statistical analysis to baseball. One of the key statistics to come out of the sabermetrics movement has been BABIP which can be described in depth if you click the link in the sketch. For better or worse though, it’s essentially a comparative statistic that reveals how lucky a batter has been in a given stretch. There is a career BABIP that is only created after 1,000 at-bats, and a players more recent numbers are compared to his career numbers to see if he’s getting luckier than usual or not.
This sketch is the beginning of what I hope to be a bigger project which is generating my own Fantasy Baseball draft software. I’d love for users to be able to have these sort of visual comparisons for a lot more fields than just BABIP.
Synthesis Day and Sketch
Synth Day
To be honest, this wasn’t too much of a struggle for me. Or the only moments in which there was a struggle was the debugging… I guess that’s always the case though. I didn’t have trouble conceptually. It was pretty rudimentary to understand that you’re replacing a variable with a sensor value and that sensor is going to have an effect on your sketch. What was difficult, and what I would like to understand a bit more of, is what exactly is happening in all of that code that we virtually copy and paste on synthesis day. That aside, the day was enjoyable for the sheer amount of possibilities it opened up including for the baseball sketch I can’t seem to get away from.
In terms of that sketch, my partner Swapna and I thought it would be cool to “become” the pitcher in my baseball sketch. Rather than have the ball automatically redraw itself once it left the frame, it would be cool to determine via the press of a button when the baseball would be thrown. Adding that definitely makes the sketch more of a game just a really…boring one. However it’s a start. Perhaps now I can introduce four different buttons each corresponding to a different pitch; button one is a fastball, button two a curve etc. My P-Comp – and hopefully ICM – midterm is going to revolve around a lot of this stuff. DO YOU KNOW ANYONE AT MLB, DANO?!
SKETCH
I admit: I am having some problems again with ICM. It’s sort of like , you said in class, the plateau is gone and the hill is in the midst of being climbed. Once again, I understand the concepts. I know what a constructor function is and how they work logically. However, I am having a lot of difficulty applying the concept to a sketch from scratch. For my sketch this week, I took some code that was in the Coding Rainbow video and made it my own a bit which is fine. I understand that’s a viable way to learn. But what I’d love to do, and what I am still trying to work on is making this from the get-go. If I don’t understand a bit better by end of week, I see some office hours in our future. NO SPORTS TALK ALLOWED.
CODE:
var bubbles = [];
function preload () {
img = loadImage(“Coaster.jpg”);
}
function setup() {
createCanvas(600, 400);
img.loadPixels();
for (var i = 0; i < 20; i++) {
bubbles[i] = new Bubble();
}
}
function mouseDragged () {
bubbles.push(new Bubble(mouseX, mouseY));
}
function draw() {
background(255);
image(img,0,0,600,400);
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].move();
bubbles[i].display();
}
if (bubbles.length > 10) {
bubbles.splice(0,1);
}
}
function Bubble(x,y) {
this.x = x;
this.y = y;
this.display = function() {
stroke(255);
fill(0);
rect(this.x,this.y-24, 25,25);
stroke(10);
strokeWeight(2);
fill(255);
ellipse(this.x+24,this.y,12,12);
ellipse(this.x, this.y, 12, 12);
};
this.move = function() {
this.x = this.x + random(-0.5, 0.5);
this.y = this.y + random(-0.5, 0.5);
};
}
My First Baseball Game
Well it’s certainly getting to that point where ICM is a challenge. I think the difficulty I’m having with it is that I am just overwhelmingly anxious about it yet when I try to pinpoint what specifically I am anxious about, I can’t. I was explaining it to my friend Patrick this way: when I eat lunch I eat my sandwich, then my chips, then my dessert. I don’t understand those people who eat chips while they’re eating those sandwich and I have a severe disdain for those monsters who put chips ON their sandwich and that’s what coding is. I understand for loops and if statements and Boolean variables independent from one another but when you put all of them in a sandwich? Uh-uh.
I decided the best way to deal with my anxiety about all of this was to start simple…keep it rudimentary, ya know. So that explains my first sketch. I originally wanted it to be people watching TV and when you clicked on the television, it turned on to show Dan-O’s face. I was getting way too stressed and not getting to my other assignments so I decided to simplify it. Instead of focusing on mousePressed or anything like that, I just kept it simple with if statements. I felt pretty confident about inserting images so I decided to put Dan-O’s face in there to try and somewhat keep with my theme.
My second sketch was one I was really proud of and one that…sort of let things click in my mind. I was taking a break from ICM and watching the end of the Orioles game. It was the top of the 9th, man on first against a division rival who we needed to beat to stay alive in the playoff race. My favorite Oriole, Hyun Soo Kim, came up to bat with a man on first down by a run, and then BAM a two run shot to right center field. I decided I had to commemorate this moment in code. I started with an image behind home plate. I inserted some variables – something I felt shaky doing – and figured out logically how to make what I wanted to make. I threw in some if statements and as I was doing so was like….man, I can make a game of my own. So I inserted a mousePressed function that made the ball appear to be hit when the mouse was clicked. To make it more difficult, I decreased the range in which you could “swing” (read: click). I also figured I needed to add sound so I used the reference library but couldn’t get it quite right. Thanks to Matt and Kat, I was able to realize my issue: I was doing function preload twice.
For the last sketch…it was sort of a failure but I figured it was a good learning experience. I used a for loop to set up my images but I want them to be able to switch places (pink on bottom, blue on top…how risqué), when mousePressed is clicked but I can’t figure out how to do it for the life of me.
Stargate
I feel like my post this week is…sort of constantly in a state of flux. I used this past week’s lessons as a chance to have some fun and play around. I’m sure in like a few hours I won’t even remember what I uploaded. That’s not to say I didn’t take this seriously, I did, I’m just having so much fun exploring that I uploaded what made me laugh the most.