Det finnes en rekke kilder for resultater og statistikk om fotball (siden dette er et yndet tema for spillere.)
Jeg har samlet sammen resultater for en rekke sesonger for blandt annet den norske eliteserien.
Disse dataene er organisert i en kommaseparert (csv) fil med linje pr kamp.
De første linjene for sesongen 2016 ser slik ut:
Denne fila ble laget ved å plukke ut alle navnene fra resultatfila for å kontrollere at alle lagnavn er skrevet riktig.
Jeg har dessuten sjekket at antall kamper stemmer, at formatet på alle kampfilene er riktig og at
poengberegningene stemmer med offisielle tabeller. På grunnlag av dette anser jeg at datagrunnlaget er riktig formatert.
Disse dataene er stabile og jeg kommer ikke til å legge inn kontroller på data i løsningen.
der XXXX er årstall fom 2000 tom 2016. Du kan bruke dem direkte fra adressene over eller du kan hente dem ned ved å
åpne dem i en nettleser og kopiere dem lokalt til din maskin.
Struktur
Det er to begreper som står fram i resonnementet: sesong og lag.
Jeg faller fort ned på den konklusjonen at jeg vil ha en klasse for hver av disse.
Disse klassene beskriver jeg i hver sin fil,
og jeg får tilsammen tre filer i løsningen: Fotballsesong.pde, sesong.pde og lag.pde.
fotballsesong.pde
fotballsesong er starten som setter opp skissen, bestemmer framRate og den eller de sesongene jeg vil animere.
// sesongen vi viser
Sesong serien;
// runden vi viser
int runde;
void setup(){
size();
lagSesong();
runde=0;
}
void lagSesong(){
serien=new Sesong(tittel og filnavn);
}
void draw(){
serien.tegn(runde);
runde+=1;
}
sesong.pde
sesong skal ha en liste over lag. sesong leser også de aktuelle filene og fordeler resultater til lagene. Et sesong-objekt tegner seg selv. Det er selve uttegningen som er den delen av koden som er mest knotete og det er denne som kan endres for å oppnå en annen visuell effekt.
Min erfaring er at slike uttegninger er en typisk "prøve og feile" prosess.
class Sesong{
ArrayList<Lag>lagListe;
String tittel;
Sesong(String tittel,String lagfil,String resultatfil){
this.tittel=tittel;
// plukker opp det vi trenger fra filer
// og oppretter lagListe
}
void tegn(int runde){
}
}
lag.pde
lag innhoder en liste over hvor mange poeng som er oppnådd pr runde,
og svarer på spørsmål om akkumulerte poen etter et antall runder.
class Lag{
String lagNavn;
IntList poeng;
Lag(String lagNavn){
this.lagNavn=lagNavn;
poeng=new IntList();
}
void leggTilKamp(String[] deler){
// bare interessert i poeng
// sjekk hjemekamp eller bortekamp
// og legg til 3,1 eller 0 ipoeng
}
int getPoengEtterRunde(int runde){
}
}
En løsning
_fotballsesong.pde
/*
Framstilling av utviklingen i poengfordelig
i rundene i en fotball sesong
*/
// sesongen, serien vi vi se på
Sesong serien;
// runden vi viser
int runde;
// er animasjonen aktiv
boolean aktiv;
void setup(){
size(600,400);
// setter opp sesongen
lagSesong();
runde=0;
aktiv=false;
frameRate(5);
}
void draw(){
background(255);
serien.tegn(runde);
if(aktiv)
runde+=1;
}
void lagSesong(){
serien=new Sesong("Eliteserien2016",
"https://borres.hiof.no/fotballdata/csv/NO/LagEliteserien2016.csv",
"https://borres.hiof.no/fotballdata/csv/NO/Eliteserien2016.csv");
}
void mouseClicked(){
aktiv=!aktiv;
if(aktiv)
if(runde >= serien.getAntallRunder())
runde=0;
}
_sesong.pde
/*
En klasse som tar seg av en liste med lag og
resultatene i en sesong. Tegner en animasjon av seg
utviklingen i sesongen
*/
class Sesong{
// lagene som er med i serien
ArrayList<Lag>lagListe;
String tittel;
int antallRunder;
int maxPoeng;
Sesong(String tittel,String lagfil,String resultatfil){
this.tittel=tittel;
// plukker opp det vi trenger fra filer
// lagene
String[] lagData=loadStrings(lagfil);
// alle kampene
String[] kampData=loadStrings(resultatfil);
// alle skal spille mot alle andre 2 ganger
antallRunder=(lagData.length-1)*2;
// sett opp lagene
lagListe=new ArrayList<Lag>(lagData.length);
for(int ix=0;ix < lagData.length;ix++){
String lagNavn=lagData[ix].trim();
// et nytt lag
Lag nyttLag=new Lag(lagNavn);
// og vi fyller det med resultatdata
for(int resix=0;resix<kampData.length;resix++){
String kampLinje=kampData[resix];
String[] deler=kampLinje.split(",");
// dato, runde,hjemmelag,hjemmemål,bortemål,bortelag
// angår denne kampen dette laget ?
if(deler[2].equals(lagNavn) ||
deler[5].equals(lagNavn)){
nyttLag.leggTilKamp(deler);
}
}
// put laget i lista
lagListe.add(nyttLag);
}
// finn max poeng, greitt når vi skal tegne
maxPoeng=0;
for(int ix=0; ix < lagListe.size(); ix++){
maxPoeng=max(maxPoeng,
lagListe.get(ix).getPoengEtterRunde(1000));
}
}
int getAntallRunder(){
return antallRunder;
}
// tegn status for en runde
void tegn(int runde){
// detnne tegnigen er den grisete
// delen av prosjektet
int margVenstre=100;
int margNede=40;
int margOppe=20;
// Plass på y-aksen skal til hvert lag
int ysteg=(height-margOppe-margNede)/lagListe.size();
// størrelsen på de vandrende kulene
float kuleDiameter=ysteg-2;
// skriv ut tittel
textAlign(RIGHT,TOP);
text(tittel,width-2,2);
// skriv ut instruksjoner
textAlign(CENTER,CENTER);
text("Klikk for å starte/stoppe animasjonen",
width/2,height-margNede/2);
// tegn yaksen med navn og kuler/streker
// selve aksen
stroke(0);
line(margVenstre,height-margNede,margVenstre,0);
// lagnavnene
fill(0);
for(int ix=0;ix < lagListe.size(); ix++){
int ypos=margOppe+ysteg*ix;
Lag laget=lagListe.get(ix);
// klipper alt for lange navn
String lagNavn=laget.getLagNavn();
if(lagNavn.length() >15)
lagNavn=lagNavn.substring(0,15);
fill(0);
textAlign(LEFT,CENTER);
text(lagNavn,2,ypos);
// strek fra y-aken til kula
// bruker map for å finne rett posisjon
float lineLength=map(laget.getPoengEtterRunde(runde),
0,maxPoeng,
0,width-margVenstre-kuleDiameter/2);
stroke(200,200,200);
line(margVenstre,ypos,margVenstre+lineLength,ypos);
// kula
stroke(255,0,0);
fill(255);
ellipse(margVenstre+lineLength,ypos,kuleDiameter,kuleDiameter);
// med tkst (antall poeng) inne i
fill(0);
textAlign(CENTER,CENTER);
text(lagListe.get(ix).getPoengEtterRunde(runde),
margVenstre+lineLength,ypos);
}
}
}
_lag.pde
/*
Et fotballag og hva de har produsert av poeng
*/
class Lag{
String lagNavn;
IntList poeng;
Lag(String lagNavn){
this.lagNavn=lagNavn;
poeng=new IntList();
}
void leggTilKamp(String[] deler){
// bare interessert i poeng
// dato, runde,hjemmelag,hjemmemål,bortemål,bortelag
int hjemmeMal=parseInt(deler[3]);
int borteMal=parseInt(deler[4]);
// 3 poeng for seier, 1 for uavgjort og 0 for tap
if(deler[2].equals(lagNavn)){
// hjemmekamp
if(hjemmeMal > borteMal)
poeng.append(3);
else if (borteMal > hjemmeMal)
poeng.append(0);
else
poeng.append(1);
}else if(deler[5].equals(lagNavn)){
// borte kamp
if(hjemmeMal > borteMal)
poeng.append(0);
else if (borteMal > hjemmeMal)
poeng.append(3);
else
poeng.append(1);
}
}
int getPoengEtterRunde(int runde){
runde=min(runde,poeng.size());
int retur=0;
for(int ix=0; ix < runde ; ix++)
retur+=poeng.get(ix);
return retur;
}
String getLagNavn(){
return lagNavn;
}
}