Today's Page Hits: 2660
I have more hair and it isn't so grey. :->
This page validates as XHTML 1.0, and will look much better in a browser that supports web standards, but it is accessible to any browser or Internet device. It was created using techniques detailed at glish.com/css/.
I bought a six seater Insta-Bench from amazon.com - they had the best price and with 3 day shipping, it still came out less than other online retailers. I got the thing, pulled it out to make sure it worked, and threw away the box.
We went to a tournament this weekend and one of the caps which secures the seat material to the frame popped off in two. I told everyone I'd superglue it back together. They told me to return it.
Instead of contacting Amazon directly, I sent an email to Insta-Bench asking for some caps. And I got a reply stating that some were in the mail. I'm really happy with their service. And their product is great as well.
There is a Petition to bring Mens soccer to the Big 12. Please add your name to the bottom line.
Found via message boards on BigSoccer.com
For soccer message boards, I've found that Infosports.com does not have much traffic. They are a good source for help with Baseball.
I'd also rate eteamz.com as being dead.
You can find a lot of flames and regional coverage at GotSoccer.com.
I've been using BigSoccer.com to get my youth soccer coaching discussions.
In Uniforms for Fall 2006, I talked about the process I used to create uniform prototypes. Well, I finally got my kids assigned and it was time to fit them out. I ended up getting the shirts and Sei product from Hobby Lobby to do the project.
I took advantage of knowing that this dye would not seep through that much. I spent about $5 a jersey and I charged the parents that much. My hand was sore from all of the pumping, I did not try any of the ideas I had presented earlier about making it easier.
While at another 3v3 tournament, I saw a team which had stenciled numbers painted on. I decided I wanted to try and get numbers on. I did not want to pay to get them done. I thought long and hard about how to apply the paint and have it seemless. I could have had a white circle with a colored number inside, but at this age, the numbers take up most of the back of the jersey.
I decided to try an experiment with one of the castoffs from before. I took some painter's tape, applied it to the shirt, and sprayed over it. There was some seepage, especially if I had two pieces in parallel. I decided to go down this route. I could always paint over the white if needed and then get real numbers applied.
Here is what a jersey looked like after the dye had been applied and right before I took off the tape. Notice how every seam is covered on the tape. One thing I did find is that the dye ran down the tape and had to be dried off. Otherwise the colors might run. You can kinda see that at the bottom of the 8.
And here is the same jersey with the tape removed. There was some seepage, I think this was one of the last to be done and my hand was cramping. Also, the applicator got slippery very easy. It does look like the blotch on the 1 was from a drip on the tape. It ran down and the material wicked the dye up under the tape.
All in all, I was happy with the shirts. When I handed them out, I pointed out that unlike most jerseys, they were not uniform. Each shirt was individualistic. I then pointed out that everyone on the team was different and we could not expect them to all play the same. I said our strength lay in knowing what every individual brought to the team. I've since made a point of stressing what the different individuals bring to the mix.
The Monster and I went to watch a bit of soccer today. Our rec season starts next week, so I dragged him to the Broken Arrow Labor Day Tournament for the competitive teams. My goal with him was to talk about how other players/teams do the things I say not to do in practice.
We watched some good U13 girl's action. It was the HFC 95 (Braunston) vs Blitz United 95 Navy. I thought the United team was more physical, they had plenty of nice shoulder tackles. The Hurricanes seemed to have more finesse and I thought they were in control of the game. The ball finally got down to the Hurricane side, there was a corner kick, and they were down 1. (Or tied, whatever, I have no idea of what the score was before I got there.)
Within a minute, they were down 2. I'm pretty sure they were losing at this point, I heard the coach telling them that they were not out of it. With halftime, we started watching some U15 boy's action. It was Claremore Freedom 92 vs WSA 92. The Freedom were getting mauled. We didn't watch much, their forwards/mids seemed to be able to get the ball, but they never worked together. We saw two quick goals - well actually we missed one as we watched a player searching for his glasses.
I'm just guessing, but they carried 18 players (max allowed) and I would guess that they have continuity problems caused by getting them all playing time. WSA was much more poised and passed.
And then, we walked across the complex to watch some U11 girls play. They were closest in age to the Monster and I wanted to see them at the start of the season. We were lucky to even see them, most of the U11 action was across the main street. We were the only ones lucky.
As we walked up, I could hear the field marshals explaining to each other that it was competitive and unlike recreational, the coaches could run the score up as high as they wanted. I think it was 9-0 at that point. We saw several quick goals and I actually got the Monster to watch and point out some problems.
I'm just going to call the teams White and Red. The Red were up and looked like a competitive team. The White were down and looked worse than the U10 rec girl teams I watched at Union last year. I actually thought I recognized a girl from Union, but no, it wasn't her.
The White team did not work as a team. On offense, they would immediately start calling for the ball when someone got it, they had no concept of offsides, and when the ball was stolen to be sent down the field, they would just stand there and pout. I'm pretty sure they couldn't maintain positions and would gravitate towards the ball like it was a planet.
On defense, they definitely had no concept of space. The defenders would stay a bit spread out, but always on the side of the field which had the ball. There was always a back door open if the Red attackers could get the ball switched.
But even worse was the goalie. She had no desire to win the ball. She would stay on the line and when the ball was inside the 6, she would stay upright. She couldn't wrap it up and she wouldn't go down low for it. When she punted, she might miss it or even when she connected, it only went a couple of feet. Goal kicks went straight across the middle, to be picked up by Red. At one point I told my son there was one which did not result in a goal. He said "Look," and Red put it in.
I'm not blaming her for this, I'm blaming the coach. He had someone in there who did not have the confidence needed. I told my son, I don't care if you get beat if you are going for the save. She got beat not going for the save. I won't put a player in at goalie if they don't want the ball. The Monster isn't aggressive enough as a field player, but put him in at goalie and he'll scrap for the ball:
In the big picture of things, the White team was the B team of their club and the Red team might have been the A team. The Red team looked like they had been playing together for much some time. I wouldn't say that they were all better athletes - I'd say that they had been an Academy team the year before.
In the short term, the White team is going to struggle in their league. I think they would struggle playing some U10 rec programs and many U10 academy teams. I think in the long run, if they stick with it, learn to trust each other, and gain some team work, they could be a good team.
However, I think the Red team actually could have bigger issues. They were up 15-0 when they pulled the reins in. I have no clue if they were playing defenders up or what their normal lineup would have been. I do know the parents were saying this was embarrassing. But what I didn't like were the smirks the girls had as they played. When they stopped trying to score, they started to play keep away. And they laughed as they made White's forwards chase the ball.
But they didn't pay attention either. They would pass into traffic or back to the goalie. And then it finally happened, they passed back to the goalie in the box, she picked it up and an indirect kick was awarded about 7 yards out. Now all of the White fielders came up. The Ref was actually kind enough (felt pity) to explain that it was an indirect kick and not a direct kick. This prompted the player to not bang it into the wall, but out and over to her team mate. She in turn bounced it off of the post what seemed like 2 times and off a defender as well. Then it went in to break the shutout.
Is the lesson learned to always persevere? to never take your foot off of their neck? or not to be arrogant? While Red lost a point in the tournament (6 for a win, 3 for first 3 goals, and 1 for a shutout), I wouldn't say it was show no mercy. No, I would say it was do not disrespect your opponent. Those girls on White were picked for a reason (including the goalie). There are other girls in rec or who quit altogether, girls who didn't make the cut for a competitive team.
In short, the White team can get better and know it. The Red team might already think that they are great and fail to improve. I'd rather be an U11 team which has a struggle than one which has peaked early.
I was looking at the script and I found a bug:
if (($father eq "" && $flname eq "") || ($mother eq "" && $glname eq "")) {
A coach is someone who doesn't have a father and a mother. We can have players registered with just a father or a mother. Sure enough, a test script shows:
GRANT THOMAS is a coach for 08B09 (age is 7) ELLEN THOMAS is a coach for 10G04 (age is 9) REX THOMAS is a coach for (age is 6)
We don't have any coaches that young. The reason this slipped by was that we went from listing individual conlicts to listing team conflicts. I.e., we abstracted the display, allowing some details to be missed. Let's fix it:
if (($father eq "" && $flname eq "") && ($mother eq "" && $glname eq "")) {
In the test script, the kids went away. Lets check how much difference this makes in the final result:
[usc@adept ~]$ ./sall2.pl people.txt > fff [usc@adept ~]$ diff eee fff 21c21 < Team 08B05 has conflicts with 12G03, 14B01, 16B02, 10G01, 10G03 --- > Team 08B05 has conflicts with 12G03, 14B01, 12B03, 16B02, 10G01, 10G03 23,24c23,24 < Team 08B08 has conflicts with 06C03 < Team 08B09 has conflicts with 12B02, 06C02 --- > Team 08B08 has conflicts with 09G04, 12B03, 06C03 > Team 08B09 has conflicts with 12B02, 06C02, 10G04 32a33 > Team 09G04 has conflicts with 08B08, 12B03 43c44 < Team 10G04 has conflicts with 14B02, 07B01, 08B04, 05C01, 14B01 --- > Team 10G04 has conflicts with 14B02, 07B01, 08B04, 05C01, 14B01, 08B09 48c49 < Team 12B03 has conflicts with 05C03, 10B03 --- > Team 12B03 has conflicts with 09G04, 08B08, 08B05, 05C03, 10B03, 16B02 61c62 < Team 14G02 has conflicts with 16B02, 06C08, 09G06, 10B02, 16B01, 12B04 --- > Team 14G02 has conflicts with 16B02, 06C08, 09G06, 10B02, 14G04, 16B01, 12B04 63c64 < Team 14G04 has conflicts with 11B01C, 10B04, 09B02, 16G03, 12G01 --- > Team 14G04 has conflicts with 11B01C, 10B04, 14G02, 09B02, 16G03, 12G01 66c67 < Team 16B02 has conflicts with 14G02, 10G02, 08B05, 12G03, 12G02, 12B01, 05C04 --- > Team 16B02 has conflicts with 14G02, 10G02, 08B05, 12G03, 12G02, 12B01, 12B03, 05C04
By the way, looking back at the data:
GRANT THOMAS is a coach for 08B09 (age is 7) ELLEN THOMAS is a coach for 10G04 (age is 9) REX THOMAS is a coach for (age is 6)
We see that some players (and also coaches, just not shown) have yet to be assigned to teams. Hence we need the test in checkConflict to make sure the tcode is not empty. But do we really do that?
sub checkConflict {
my($tcode1, $tcode2) = @_;
if ($conflict{$tcode1}) {
if (!($conflict{$tcode1} =~ /$tcode2/)) {
$conflict{$tcode1} = $conflict{$tcode1} . ", " . $tcode2;
}
} else {
$conflict{$tcode1} = $tcode2;
}
}
Oh, I see it, we do it up in the callers:
if ($match && !$tmatch && ($ar[$i]{tcode} ne "") && ($ar[$j]{tcode} ne "")) {
addConflicts($ar[$i]{tcode}, $ar[$j]{tcode});
}
It might be better to move those checks down, like this:
sub checkConflict {
my($tcode1, $tcode2) = @_;
if (!($tcode1 eq "" || $tcode2 eq "")) {
if ($conflict{$tcode1}) {
if (!($conflict{$tcode1} =~ /$tcode2/)) {
$conflict{$tcode1} = $conflict{$tcode1} . ", " . $tcode2;
}
} else {
$conflict{$tcode1} = $tcode2;
}
}
}
I'm not going to make the change right now, it is just a consideration. In production code, I'd make the change right away. You need to protect your data where you use it.
In Yet more Perl for coaching, I went through the evolution of a Perl script to detect conflicts between siblings on multiple teams. One thing I tried to do was throw out the coach data (no parents). This shaving was wrong, we also need to consider conflicts caused by a coach who doesn't have any kids playing. They do not want to be coaching multiple teams at once.
I'm going to skip ahead and present close to the finished product. There were interesting things which happened, but I want to concentrate on deciding on how to make functions. Here is the base script:
#! /usr/bin/perl
sub stripArray {
my(@ar) = @_;
for ($i = 0; $i < @ar; $i++) {
if ($ar[$i] =~ /^"(.*)"$/) {
($ar[$i]) = $ar[$i] =~ /"(.*)"/;
}
}
@ar;
}
do 'getthead.pl';
open(LNG_FILE, $ARGV[0]) || die "Can't open LNG_FILE: $!\n";
# Determine the Column Names
do main'read_txtfile_format(*LNG_FILE, *languages); #' Hack to get color correct in vim...
my(@arSibs, @arCoaches);
lang: while (<LNG_FILE>) {
next lang if (/^#/ || /^!/);
eval "($languages) = stripArray(split(/[,\n]/))";
if (($father eq "" && $flname eq "") || ($mother eq "" && $glname eq "")) {
my(%curr) = (
'first' => $fname,
'last' => $lname,
'team' => $team,
'tcode' => $tcode);
#
# Note that a coach can do multiple teams.
#
push(@arCoaches, \%curr);
} elsif (($father ne "" && $flname ne "") || ($mother ne "" && $glname ne "")) {
my(%curr) = (
'first' => $fname,
'last' => $lname,
'team' => $team,
'tcode' => $tcode,
'ffirst' => $father,
'flast' => $flname,
'fmatch' => 0,
'mfirst' => $mother,
'mlast' => $glname,
'mmatch' => 0);
push(@arSibs, \%curr);
}
}
close LANG_FILE;
sub sortByLast {
$$a{last} cmp $$b{last};
}
my(@ar) = sort(sortByLast @arSibs);
my(%conflict);
#
# First get players taken care of...
#
for ($i = 0; $i < @ar; $i++) {
$match = 0;
#
# First consider other players
#
for ($j = 0; $j < @ar; $j++) {
if ($i == $j) {
next;
}
$fmatch = ($ar[$i]{ffirst} eq $ar[$j]{ffirst}) &&
($ar[$i]{flast} eq $ar[$j]{flast}) &&
($ar[$i]{ffirst} ne "") &&
($ar[$i]{flast} ne "");
$mmatch = ($ar[$i]{mfirst} eq $ar[$j]{mfirst}) &&
($ar[$i]{mlast} eq $ar[$j]{mlast}) &&
($ar[$i]{mfirst} ne "") &&
($ar[$i]{mlast} ne "");;
$tmatch = ($ar[$i]{tcode} eq $ar[$j]{tcode});
if (($fmatch || $mmatch) && !$tmatch && ($ar[$i]{tcode} ne "") &&
($ar[$j]{tcode} ne "")) {
if ($conflict{$ar[$i]{tcode}}) {
if (!($conflict{$ar[$i]{tcode}} =~ /$ar[$j]{tcode}/)) {
$conflict{$ar[$i]{tcode}} = $conflict{$ar[$i]{tcode}} .
", " . $ar[$j]{tcode};
}
} else {
$conflict{$ar[$i]{tcode}} = $ar[$j]{tcode};
}
if ($conflict{$ar[$j]{tcode}}) {
if (!($conflict{$ar[$j]{tcode}} =~ /$ar[$i]{tcode}/)) {
$conflict{$ar[$j]{tcode}} = $conflict{$ar[$j]{tcode}} .
", " . $ar[$i]{tcode};
}
} else {
$conflict{$ar[$j]{tcode}} = $ar[$i]{tcode};
}
}
}
#
# Now consider coaches
#
for ($j = 0; $j < @arCoaches; $j++) {
$fmatch = ($ar[$i]{ffirst} eq $arCoaches[$j]{first}) &&
($ar[$i]{flast} eq $arCoaches[$j]{last}) &&
($ar[$i]{ffirst} ne "") &&
($ar[$i]{flast} ne "");
$mmatch = ($ar[$i]{mfirst} eq $arCoaches[$j]{first}) &&
($ar[$i]{mlast} eq $arCoaches[$j]{last}) &&
($ar[$i]{mfirst} ne "") &&
($ar[$i]{mlast} ne "");;
$tmatch = ($ar[$i]{tcode} eq $arCoaches[$j]{tcode});
if (($fmatch || $mmatch) && !$tmatch && ($ar[$i]{tcode} ne "") &&
($arCoaches[$j]{tcode} ne "")) {
if ($conflict{$ar[$i]{tcode}}) {
if (!($conflict{$ar[$i]{tcode}} =~ /$arCoaches[$j]{tcode}/)) {
$conflict{$ar[$i]{tcode}} = $conflict{$ar[$i]{tcode}} .
", " . $arCoaches[$j]{tcode};
}
} else {
$conflict{$ar[$i]{tcode}} = $arCoaches[$j]{tcode};
}
if ($conflict{$arCoaches[$j]{tcode}}) {
if (!($conflict{$arCoaches[$j]{tcode}} =~ /$ar[$i]{tcode}/)) {
$conflict{$arCoaches[$j]{tcode}} =
$conflict{$arCoaches[$j]{tcode}} .
", " . $ar[$i]{tcode};
}
} else {
$conflict{$arCoaches[$j]{tcode}} = $ar[$i]{tcode};
}
}
}
}
#
# First get players taken care of...
#
my(@ar) = sort(sortByLast @arCoaches);
for ($i = 0; $i < @ar; $i++) {
for ($j = $i + 1; $j < @ar; $j++) {
$match = ($ar[$i]{first} eq $ar[$j]{first}) &&
($ar[$i]{last} eq $ar[$j]{last}) &&
($ar[$i]{first} ne "") &&
($ar[$i]{last} ne "");
$tmatch = ($ar[$i]{tcode} eq $ar[$j]{tcode});
if ($match && !$tmatch && ($ar[$i]{tcode} ne "") && ($ar[$j]{tcode} ne "")) {
if ($conflict{$ar[$i]{tcode}}) {
if (!($conflict{$ar[$i]{tcode}} =~ /$ar[$j]{tcode}/)) {
$conflict{$ar[$i]{tcode}} = $conflict{$ar[$i]{tcode}} .
", " . $ar[$j]{tcode};
}
} else {
$conflict{$ar[$i]{tcode}} = $ar[$j]{tcode};
}
if ($conflict{$ar[$j]{tcode}}) {
if (!($conflict{$ar[$j]{tcode}} =~ /$ar[$i]{tcode}/)) {
$conflict{$ar[$j]{tcode}} = $conflict{$ar[$j]{tcode}} .
", " . $ar[$i]{tcode};
}
} else {
$conflict{$ar[$j]{tcode}} = $ar[$i]{tcode};
}
}
}
}
foreach $key (sort(keys(%conflict))) {
print "Team $key has conflicts with " . $conflict{$key} . "\n";
}
exit(0);
Some sample output:
[usc@adept ~]$ ./scheds_all.pl people.txt | head Team 05C01 has conflicts with 10G04, 08B04, 16B01 Team 05C02 has conflicts with 08B03, 10B01 Team 05C03 has conflicts with 12B03 Team 05C04 has conflicts with 16B02, 16G03 Team 06C01 has conflicts with 09B01 Team 06C02 has conflicts with 08B09 Team 06C03 has conflicts with 08B08 Team 06C05 has conflicts with 09B01 Team 06C08 has conflicts with 14G02 Team 06C15 has conflicts with 14B01C
We've basically got the same code in 3 places - where we build up the conflict string. At first glance, it looks hard to functionalize - the coaches' array has a different containing structure. (I'm more used to C, which would really make this difficult.) But as we look at it, both structures share a tcode. If we examine it more carefully, we don't care at all about the arrays - we just care about their contents. The following changes give us new functionality:
sub addConflicts {
my($tcode1, $tcode2) = @_;
if ($conflict{$tcode1}) {
if (!($conflict{$tcode1} =~ /$tcode2/)) {
$conflict{$tcode1} = $conflict{$tcode1} . ", " . $tcode2;
}
} else {
$conflict{$tcode1} = $tcode2;
}
if ($conflict{$tcode2}) {
if (!($conflict{$tcode2} =~ /$tcode1/)) {
$conflict{$tcode2} = $conflict{$tcode2} . ", " . $tcode1;
}
} else {
$conflict{$tcode2} = $tcode1;
}
}
do 'getthead.pl';
...
#
# First get players taken care of...
#
for ($i = 0; $i < @ar; $i++) {
$match = 0;
#
# First consider other players
#
for ($j = 0; $j < @ar; $j++) {
if ($i == $j) {
next;
}
$fmatch = ($ar[$i]{ffirst} eq $ar[$j]{ffirst}) &&
($ar[$i]{flast} eq $ar[$j]{flast}) &&
($ar[$i]{ffirst} ne "") &&
($ar[$i]{flast} ne "");
$mmatch = ($ar[$i]{mfirst} eq $ar[$j]{mfirst}) &&
($ar[$i]{mlast} eq $ar[$j]{mlast}) &&
($ar[$i]{mfirst} ne "") &&
($ar[$i]{mlast} ne "");;
$tmatch = ($ar[$i]{tcode} eq $ar[$j]{tcode});
if (($fmatch || $mmatch) && !$tmatch && ($ar[$i]{tcode} ne "") &&
($ar[$j]{tcode} ne "")) {
addConflicts($ar[$i]{tcode}, $ar[$j]{tcode});
}
}
#
# Now consider coaches
#
for ($j = 0; $j < @arCoaches; $j++) {
$fmatch = ($ar[$i]{ffirst} eq $arCoaches[$j]{first}) &&
($ar[$i]{flast} eq $arCoaches[$j]{last}) &&
($ar[$i]{ffirst} ne "") &&
($ar[$i]{flast} ne "");
$mmatch = ($ar[$i]{mfirst} eq $arCoaches[$j]{first}) &&
($ar[$i]{mlast} eq $arCoaches[$j]{last}) &&
($ar[$i]{mfirst} ne "") &&
($ar[$i]{mlast} ne "");;
$tmatch = ($ar[$i]{tcode} eq $arCoaches[$j]{tcode});
if (($fmatch || $mmatch) && !$tmatch && ($ar[$i]{tcode} ne "") &&
($arCoaches[$j]{tcode} ne "")) {
addConflicts($ar[$i]{tcode}, $arCoaches[$j]{tcode});
}
}
}
#
# First get players taken care of...
#
my(@ar) = sort(sortByLast @arCoaches);
for ($i = 0; $i < @ar; $i++) {
for ($j = $i + 1; $j < @ar; $j++) {
$match = ($ar[$i]{first} eq $ar[$j]{first}) &&
($ar[$i]{last} eq $ar[$j]{last}) &&
($ar[$i]{first} ne "") &&
($ar[$i]{last} ne "");
$tmatch = ($ar[$i]{tcode} eq $ar[$j]{tcode});
if ($match && !$tmatch && ($ar[$i]{tcode} ne "") && ($ar[$j]{tcode} ne "")) {
addConflicts($ar[$i]{tcode}, $ar[$j]{tcode});
}
}
}
foreach $key (sort(keys(%conflict))) {
print "Team $key has conflicts with " . $conflict{$key} . "\n";
}
The code is much easier to read, further abstraction can take place, but does it work?
[usc@adept ~]$ ./sall.pl people.txt | head [usc@adept ~]$
What is wrong? My first thought was that %conflict was the problem. It is declared after the function. What happens if we take:
my(@ar) = sort(sortByLast @arSibs); my(%conflict); # # First get players taken care of... #
And move the declaration to:
my(%conflict);
sub addConflicts {
Well, I was right:
[usc@adept ~]$ ./sall.pl people.txt | head Team 05C01 has conflicts with 10G04, 08B04, 16B01 Team 05C02 has conflicts with 08B03, 10B01 Team 05C03 has conflicts with 12B03 Team 05C04 has conflicts with 16B02, 16G03 Team 06C01 has conflicts with 09B01 Team 06C02 has conflicts with 08B09 Team 06C03 has conflicts with 08B08 Team 06C05 has conflicts with 09B01 Team 06C08 has conflicts with 14G02 Team 06C15 has conflicts with 14B01C
And a full diff of the output of both versions matches up. We really need to make addConflicts more robust. Again we have the same basic code in two places. What if we wanted to change the way we stored the list?
my(%conflict);
sub checkConflict {
my($tcode1, $tcode2) = @_;
if ($conflict{$tcode1}) {
if (!($conflict{$tcode1} =~ /$tcode2/)) {
$conflict{$tcode1} = $conflict{$tcode1} . ", " . $tcode2;
}
} else {
$conflict{$tcode1} = $tcode2;
}
}
sub addConflicts {
my($tcode1, $tcode2) = @_;
checkConflict($tcode1, $tcode2);
checkConflict($tcode2, $tcode1);
}
And a check of the data:
[usc@adept ~]$ ./sall2.pl people.txt | head Team 05C01 has conflicts with 10G04, 08B04, 16B01 Team 05C02 has conflicts with 08B03, 10B01 Team 05C03 has conflicts with 12B03 Team 05C04 has conflicts with 16B02, 16G03 Team 06C01 has conflicts with 09B01 Team 06C02 has conflicts with 08B09 Team 06C03 has conflicts with 08B08 Team 06C05 has conflicts with 09B01 Team 06C08 has conflicts with 14G02 Team 06C15 has conflicts with 14B01C
I'm really satisfied with the script for now. I could add an option to only check for conflicts for coaches and not all parents. I.e., we only care to help those people who volunteer their time. Basically, we just need to make entering the First consider other players optional.
And here is the final script:
#! /usr/bin/perl
sub stripArray {
my(@ar) = @_;
for ($i = 0; $i < @ar; $i++) {
if ($ar[$i] =~ /^"(.*)"$/) {
($ar[$i]) = $ar[$i] =~ /"(.*)"/;
}
}
@ar;
}
my(%conflict);
sub checkConflict {
my($tcode1, $tcode2) = @_;
if ($conflict{$tcode1}) {
if (!($conflict{$tcode1} =~ /$tcode2/)) {
$conflict{$tcode1} = $conflict{$tcode1} . ", " . $tcode2;
}
} else {
$conflict{$tcode1} = $tcode2;
}
}
sub addConflicts {
my($tcode1, $tcode2) = @_;
checkConflict($tcode1, $tcode2);
checkConflict($tcode2, $tcode1);
}
do 'getthead.pl';
open(LNG_FILE, $ARGV[0]) || die "Can't open LNG_FILE: $!\n";
# Determine the Column Names
do main'read_txtfile_format(*LNG_FILE, *languages); #' Hack to get color correct in vim...
my(@arSibs, @arCoaches);
lang: while (<LNG_FILE>) {
next lang if (/^#/ || /^!/);
eval "($languages) = stripArray(split(/[,\n]/))";
if (($father eq "" && $flname eq "") || ($mother eq "" && $glname eq "")) {
my(%curr) = (
'first' => $fname,
'last' => $lname,
'team' => $team,
'tcode' => $tcode);
#
# Note that a coach can do multiple teams.
#
push(@arCoaches, \%curr);
} elsif (($father ne "" && $flname ne "") || ($mother ne "" && $glname ne "")) {
my(%curr) = (
'first' => $fname,
'last' => $lname,
'team' => $team,
'tcode' => $tcode,
'ffirst' => $father,
'flast' => $flname,
'fmatch' => 0,
'mfirst' => $mother,
'mlast' => $glname,
'mmatch' => 0);
push(@arSibs, \%curr);
}
}
close LANG_FILE;
sub sortByLast {
$$a{last} cmp $$b{last};
}
my(@ar) = sort(sortByLast @arSibs);
#
# First get players taken care of...
#
for ($i = 0; $i < @ar; $i++) {
$match = 0;
#
# First consider other players
#
for ($j = 0; $j < @ar; $j++) {
if ($i == $j) {
next;
}
$fmatch = ($ar[$i]{ffirst} eq $ar[$j]{ffirst}) &&
($ar[$i]{flast} eq $ar[$j]{flast}) &&
($ar[$i]{ffirst} ne "") &&
($ar[$i]{flast} ne "");
$mmatch = ($ar[$i]{mfirst} eq $ar[$j]{mfirst}) &&
($ar[$i]{mlast} eq $ar[$j]{mlast}) &&
($ar[$i]{mfirst} ne "") &&
($ar[$i]{mlast} ne "");;
$tmatch = ($ar[$i]{tcode} eq $ar[$j]{tcode});
if (($fmatch || $mmatch) && !$tmatch && ($ar[$i]{tcode} ne "") &&
($ar[$j]{tcode} ne "")) {
addConflicts($ar[$i]{tcode}, $ar[$j]{tcode});
}
}
#
# Now consider coaches
#
for ($j = 0; $j < @arCoaches; $j++) {
$fmatch = ($ar[$i]{ffirst} eq $arCoaches[$j]{first}) &&
($ar[$i]{flast} eq $arCoaches[$j]{last}) &&
($ar[$i]{ffirst} ne "") &&
($ar[$i]{flast} ne "");
$mmatch = ($ar[$i]{mfirst} eq $arCoaches[$j]{first}) &&
($ar[$i]{mlast} eq $arCoaches[$j]{last}) &&
($ar[$i]{mfirst} ne "") &&
($ar[$i]{mlast} ne "");;
$tmatch = ($ar[$i]{tcode} eq $arCoaches[$j]{tcode});
if (($fmatch || $mmatch) && !$tmatch && ($ar[$i]{tcode} ne "") &&
($arCoaches[$j]{tcode} ne "")) {
addConflicts($ar[$i]{tcode}, $arCoaches[$j]{tcode});
}
}
}
#
# Now find conflicts by the coach...
#
my(@ar) = sort(sortByLast @arCoaches);
for ($i = 0; $i < @ar; $i++) {
for ($j = $i + 1; $j < @ar; $j++) {
$match = ($ar[$i]{first} eq $ar[$j]{first}) &&
($ar[$i]{last} eq $ar[$j]{last}) &&
($ar[$i]{first} ne "") &&
($ar[$i]{last} ne "");
$tmatch = ($ar[$i]{tcode} eq $ar[$j]{tcode});
if ($match && !$tmatch && ($ar[$i]{tcode} ne "") && ($ar[$j]{tcode} ne "")) {
addConflicts($ar[$i]{tcode}, $ar[$j]{tcode});
}
}
}
foreach $key (sort(keys(%conflict))) {
print "Team $key has conflicts with " . $conflict{$key} . "\n";
}
exit(0);
I'm about to start scheduling the games for next season. One thing I want to do is to make sure that if a coach has multiple kids, I don't schedule them all at the same time. So, I need to find out which people have multiple kids. The database will tell me that.
The first pass is to find out the information I need:
#! /usr/bin/perl
do 'getthead.pl';
open(LNG_FILE, $ARGV[0]) || die "Can't open LNG_FILE: $!\n";
# Determine the Column Names
do main'read_txtfile_format(*LNG_FILE, *languages); #' Hack to get color correct in vim...
lang: while (<LNG_FILE>) {
next lang if (/^#/ || /^!/);
eval "($languages) = split(/[,\n]/)";
if ($age < 20) {
print "$fname $lname $position |$father $flname| |$mother $glname|\n";
} else {
print "COACH: $fname $lname $position |$father $flname| |$mother $glname|\n";
}
}
close LANG_FILE;
exit(0);
Quickly, I'm guessing all coaches will be over 20. I know that won't work for all of them - but it is good enough for now. Here is some sample output:
[usc@adept ~]$ ./scheds.pl p.txt COACH: "TOMPER" "HAYNES" "" | ""| |"" ""| "MONSTER" "HAYNES" "" | "HAYNES"| |"MOMA" "The HAYNES"|
Okay, I want those " gone:
sub stripQuotes {
my($raw) = @_;
($raw) = $raw =~ /"(.*)"/;
$raw;
}
$fname = stripQuotes($fname);
$lname = stripQuotes($lname);
$father = stripQuotes($lname);
if ($age < 20) {
print "$fname $lname $position |$father $flname| |$mother $glname|\n";
} else {
print "COACH: $fname $lname $position |$father $flname| |$mother $glname|\n";
}
Which gives:
[usc@adept ~]$ ./scheds.pl p.txt COACH: TOMPER HAYNES "" | ""| |"" ""| MONSTER HAYNES "" | "HAYNES"| |"MOMA" "The HAYNES"|
A function is great, but invoking it every time is going to get old. It would be best if this was done automatically during the eval. Hmm, lets look at some raw data:
..."F06|S07| |","X",F,/ /,/ /,F,F,05/17/2005,49218,/ /,0...
For any given field, it might have a " or it may not. I don't want to hardcode this into the eval. I want to be able to use this on any standard CSV file out there. I could go look for a library, but where is the fun in that I ask you?
Lets look at the key lines:
do 'getthead.pl';
do main'read_txtfile_format(*LNG_FILE, *languages); #' Hack to get color correct in vim...
lang: while (<LNG_FILE>) {
next lang if (/^#/ || /^!/);
eval "($languages) = split(/[,\n]/)";
}
What type of data structure is $languages? It has to contain the names of the fields. Let's see what happens if we try this in the loop:
print "$languages\n";
exit(0);
We get:
[usc@adept ~]$ ./scheds.pl p.txt $fname, $lname, $mname, $street, ... , $fvolunteer, $gvolunteer
We can either work with ($languages) and another eval after the statement OR make the eval more complex. Let's try mucking with the eval statement first:
sub stripArray {
my(@ar) = @_;
for ($i = 0; $i < @ar; $i++) {
($ar[i]) = $ar[i] =~ /"(.*)"/;
}
@ar;
}
eval "($languages) = stripArray(split(/[,\n]/))";
Which yields:
[usc@adept ~]$ ./scheds.pl p.txt COACH: "HAYNES" "" |"" ""| |"" ""| "HAYNES" "" |"TOMPER" "HAYNES"| |"MOMA" "The HAYNES"|
For grins, lets take a quick diversion and see what happens with a second eval:
eval "($languages) = split(/[,\n]/)";
eval "@ar = ($languages); for ($i = 0; $i < @ar; $i++) { ($ar[i]) = $ar[i] =~ /\"(.*)\"/;}";
And it too fails:
[usc@adept ~]$ ./s.pl p.txt COACH: "TOMPER" "HAYNES" "" |"" ""| |"" ""| "MONSTER" "HAYNES" "" |"TOMPER" "HAYNES"| |"MOMA" "The HAYNES"|
Ack, I have $ar[i] instead of $ar[$i]. Fixing it does not matter. Double frak! I did the same thing with the first example! Let's try it again:
sub stripArray {
my(@ar) = @_;
for ($i = 0; $i < @ar; $i++) {
($ar[$i]) = $ar[$i] =~ /"(.*)"/;
}
@ar;
}
eval "($languages) = stripArray(split(/[,\n]/))";
And it yields:
TOMPER HAYNES | | | | MONSTER HAYNES |TOMPER HAYNES| |MOMA The HAYNES|
It is harder to detect NULLS, but it will be much easier to work with the data. But, I've got another bug. I decided to check and see what would happen if I tried to print a field which did not have quotes to start with:
if ($age < 20) {
print "$fname $lname $age $position |$father $flname| |$mother $glname|\n";
} else {
print "COACH: $fname $age $lname $position |$father $flname| |$mother $glname|\n";
}
Which gives:
[usc@adept ~]$ ./scheds.pl p.txt TOMPER HAYNES | | | | MONSTER HAYNES |TOMPER HAYNES| |MOMA The HAYNES|
I had this working last night and the fix is pretty simple:
sub stripArray {
my(@ar) = @_;
for ($i = 0; $i < @ar; $i++) {
if ($ar[$i] =~ /^"(.*)"$/) {
($ar[$i]) = $ar[$i] =~ /"(.*)"/;
}
}
@ar;
}
I.e., only change it if it has a start and end quote:
[usc@adept ~]$ ./scheds.pl p.txt COACH: TOMPER 29 HAYNES | | | | MONSTER HAYNES 9 |TOMPER HAYNES| |MOMA The HAYNES|
No, I'm not 29, but I don't want to give away too much info. ;)
Now I want to detect conflicts, which means storing lines and then going back over them. I noticed that coaches do not have fathers and mothers. That can be used to pull them out. Here is the first pass at detecting conflicts:
my(@arSibs);
lang: while (<LNG_FILE>) {
next lang if (/^#/ || /^!/);
eval "($languages) = stripArray(split(/[,\n]/))";
if (($father ne "" && $flname ne "") || ($mother ne "" && $glname ne "")) {
if ($sex eq "M") {
$i = "B";
} else {
$i = "G";
}
my(%curr) = (
'name' => $fname,
'last' => $lname,
'agegroup' => "U" . $age . $i,
'ffirst' => $father,
'flast' => $flname,
'mfirst' => $mother,
'mlast' => $glname);
push(@arSibs, \%curr);
}
}
close LANG_FILE;
sub sortByLast {
$$a{last} cmp $$b{last};
}
my(@ar) = sort(sortByLast @arSibs);
for ($i = 0; $i < @ar; $i++) {
$match = 0;
for ($j = $i + 1; $j < @ar; $j++) {
if ((($ar[$i]{ffirst} eq $ar[$j]{ffirst}) &&
($ar[$i]{flast} eq $ar[$j]{flast})) ||
(($ar[$i]{mfirst} eq $ar[$j]{mfirst}) &&
($ar[$i]{mlast} eq $ar[$j]{mlast}))) {
if ($match == 0) {
$match = 1;
print $ar[$i]{name} . " " . $ar[$i]{last} . " " . $ar[$i]{agegroup} .
" has conflicts:\n";
}
print $ar[$j]{name} . " " . $ar[$j]{last} . " " . $ar[$j]{agegroup} . "\n";
}
}
}
We create an array of structures and do a single pass to detect if there is another entry with a common parent. Note that it is okay for the fathers to be the same for one child, but the mothers to be different - stuff happens. A copy of the Monster and a quick test yields:
[usc@adept ~]$ ./scheds.pl p.txt MONSTER HAYNES U9B has conflicts: MONSTER THE U9B
Look what happens as we add a sister:
[usc@adept ~]$ ./scheds.pl p.txt MONSTER HAYNES U9B has conflicts: MONSTER LITTLE U9G MONSTER THE U9B MONSTER LITTLE U9G has conflicts: MONSTER THE U9B
We already know that THE and LITTLE have a conflict. What we need to do is record when a parent has been scanned. Note that since we only have the parent's names, we cannot tell if TOMPER HAYNES is unique. If we adjust the script as:
my(%curr) = (
'name' => $fname,
'last' => $lname,
'agegroup' => "U" . $age . $i,
'ffirst' => $father,
'flast' => $flname,
'fmatch' => 0,
'mfirst' => $mother,
'mlast' => $glname,
'mmatch' => 0);
for ($i = 0; $i < @ar; $i++) {
$match = 0;
for ($j = $i + 1; $j < @ar; $j++) {
$fmatch = ($ar[$i]{ffirst} eq $ar[$j]{ffirst}) &&
($ar[$i]{flast} eq $ar[$j]{flast}) &&
($ar[$j]{fmatch} == 0);
if ($fmatch) {
$ar[$j]{fmatch} = 1;
}
$mmatch = ($ar[$i]{mfirst} eq $ar[$j]{mfirst}) &&
($ar[$i]{mlast} eq $ar[$j]{mlast}) &&
($ar[$j]{mmatch} == 0);
if ($mmatch) {
$ar[$j]{mmatch} = 1;
}
if ($fmatch || $mmatch) {
if ($match == 0) {
$match = 1;
print $ar[$i]{name} . " " . $ar[$i]{last} . " " . $ar[$i]{agegroup} .
" has conflicts:\n";
}
print $ar[$j]{name} . " " . $ar[$j]{last} . " " . $ar[$j]{agegroup} . "\n";
}
}
}
As we find a matching parent, we mark them off. Since we have to check the match twice (once to report and once to record), we use a local variable. Note that the corresponding variable is only set if the parent has not been recorded.
When we try it on the small dataset, we get:
[usc@adept ~]$ ./scheds.pl p.txt MONSTER HAYNES U9B has conflicts: MONSTER LITTLE U9G MONSTER THE U9B
One of the things I'm trying to push here is that you have to unit test. What happens if my worst nightmare was to come true and I had a child whose mother was not my wife. And to make the resulting battle fair, she had one whose father was not me?
It actually depends on how they are entered. If my Monster is first:
[usc@adept ~]$ ./scheds.pl p.txt MONSTER HAYNES U9B has conflicts: MONSTER THE U9B MONSTER LITTLE U9G MONSTER CRINGE U9G MONSTER CLONE U9G
But if one of the non-aligned ones is first, we could get:
[usc@adept ~]$ ./scheds.pl p.txt MONSTER CLONE U9G has conflicts: MONSTER HAYNES U9B MONSTER LITTLE U9G MONSTER THE U9B MONSTER CRINGE U9G has conflicts: MONSTER HAYNES U9B MONSTER LITTLE U9G MONSTER THE U9B
The point is that unless we invest a lot of time into getting all of the permutations correct, we are going to gloss over some information. We ought to print out which parent is causing the conflict:
print "\t" . $ar[$j]{name} . " " . $ar[$j]{last} . " " . $ar[$j]{agegroup};
if ($mmatch) {
print "(" . $ar[$i]{mfirst} . " " . $ar[$i]{mlast} . ")";
}
if ($fmatch) {
print "(" . $ar[$i]{ffirst} . " " . $ar[$i]{flast} . ")";
}
print "\n";
Which would show us:
[usc@adept ~]$ ./scheds.pl p.txt
MONSTER CLONE U9G has conflicts:
MONSTER HAYNES U9B(MOMA The HAYNES)
MONSTER LITTLE U9G(MOMA The HAYNES)
MONSTER THE U9B(MOMA The HAYNES)
MONSTER CRINGE U9G has conflicts:
MONSTER HAYNES U9B(TOMPER HAYNES)
MONSTER LITTLE U9G(TOMPER HAYNES)
MONSTER THE U9B(TOMPER HAYNES)
Or
[usc@adept ~]$ ./scheds.pl p.txt
MONSTER HAYNES U9B has conflicts:
MONSTER THE U9B(MOMA The HAYNES)(TOMPER HAYNES)
MONSTER LITTLE U9G(MOMA The HAYNES)(TOMPER HAYNES)
MONSTER CRINGE U9G(TOMPER HAYNES)
MONSTER CLONE U9G(MOMA The HAYNES)
Note how a simple boolean gets swamped (we would expect both parents in the first example). It might be better to remove the boolean and try to do some final coorelation by eye:
[usc@adept ~]$ ./scheds.pl p.txt
MONSTER CLONE U9G has conflicts:
MONSTER HAYNES U9B(MOMA The HAYNES)
MONSTER LITTLE U9G(MOMA The HAYNES)
MONSTER THE U9B(MOMA The HAYNES)
MONSTER CRINGE U9G has conflicts:
MONSTER HAYNES U9B(TOMPER HAYNES)
MONSTER LITTLE U9G(TOMPER HAYNES)
MONSTER THE U9B(TOMPER HAYNES)
MONSTER HAYNES U9B has conflicts:
MONSTER LITTLE U9G(MOMA The HAYNES)(TOMPER HAYNES)
MONSTER THE U9B(MOMA The HAYNES)(TOMPER HAYNES)
MONSTER LITTLE U9G has conflicts:
MONSTER THE U9B(MOMA The HAYNES)(TOMPER HAYNES)
But it might be even better to change the loop construct to:
for ($i = 0; $i < @ar; $i++) {
$match = 0;
for ($j = 0; $j < @ar; $j++) {
if ($i == $j) {
next;
}
To get a full report for each player:
[usc@adept ~]$ ./scheds_all.pl p.txt
MONSTER CLONE U9G has conflicts:
MONSTER HAYNES U9B(MOMA The HAYNES)
MONSTER LITTLE U9G(MOMA The HAYNES)
MONSTER THE U9B(MOMA The HAYNES)
MONSTER CRINGE U9G has conflicts:
MONSTER HAYNES U9B(TOMPER HAYNES)
MONSTER LITTLE U9G(TOMPER HAYNES)
MONSTER THE U9B(TOMPER HAYNES)
MONSTER HAYNES U9B has conflicts:
MONSTER CLONE U9G(MOMA The HAYNES)
MONSTER CRINGE U9G(TOMPER HAYNES)
MONSTER LITTLE U9G(MOMA The HAYNES)(TOMPER HAYNES)
MONSTER THE U9B(MOMA The HAYNES)(TOMPER HAYNES)
MONSTER LITTLE U9G has conflicts:
MONSTER CLONE U9G(MOMA The HAYNES)
MONSTER CRINGE U9G(TOMPER HAYNES)
MONSTER HAYNES U9B(MOMA The HAYNES)(TOMPER HAYNES)
MONSTER THE U9B(MOMA The HAYNES)(TOMPER HAYNES)
MONSTER THE U9B has conflicts:
MONSTER CLONE U9G(MOMA The HAYNES)
MONSTER CRINGE U9G(TOMPER HAYNES)
MONSTER HAYNES U9B(MOMA The HAYNES)(TOMPER HAYNES)
MONSTER LITTLE U9G(MOMA The HAYNES)(TOMPER HAYNES)
Before I give you the final script - there is one more bug I found. When we enter something into the array, we check to see if at least one parent has a valid entry. What happens if a player has only one parent recorded?
CLOPPER HALLS U11B has conflicts:
JAK SPRAT U13B( )
SAM ASPEN U8G( )
EDMUND LOWER U8B( )
Not what we want. We could modify the match check to be:
$fmatch = ($ar[$i]{ffirst} eq $ar[$j]{ffirst}) &&
($ar[$i]{flast} eq $ar[$j]{flast}) &&
($ar[$i]{ffirst} ne "") &&
($ar[$i]{flast} ne "");
$mmatch = ($ar[$i]{mfirst} eq $ar[$j]{mfirst}) &&
($ar[$i]{mlast} eq $ar[$j]{mlast}) &&
($ar[$i]{mfirst} ne "") &&
($ar[$i]{mlast} ne "");;
To wrap it up, I'm going to give the complete script which gives all of the poential conflicts. I'm actually not done with it, but the database is not finished. I really don't care about individual conflicts - I want to know about team conflicts. For example, if two siblings are on the same team and there are no other siblings, then no conflict exists. But, I'll get to that once the data has all been entered!
The getthead.pl script was presented in Some more Perl for coaching.
#! /usr/bin/perl
sub stripArray {
my(@ar) = @_;
for ($i = 0; $i < @ar; $i++) {
if ($ar[$i] =~ /^"(.*)"$/) {
($ar[$i]) = $ar[$i] =~ /"(.*)"/;
}
}
@ar;
}
do 'getthead.pl';
open(LNG_FILE, $ARGV[0]) || die "Can't open LNG_FILE: $!\n";
# Determine the Column Names
do main'read_txtfile_format(*LNG_FILE, *languages); #' Hack to get color correct in vim...
my(@arSibs);
lang: while (<LNG_FILE>) {
next lang if (/^#/ || /^!/);
eval "($languages) = stripArray(split(/[,\n]/))";
if (($father ne "" && $flname ne "") || ($mother ne "" && $glname ne "")) {
if ($sex eq "M") {
$i = "B";
} else {
$i = "G";
}
my(%curr) = (
'name' => $fname,
'last' => $lname,
'agegroup' => "U" . $age . $i,
'ffirst' => $father,
'flast' => $flname,
'fmatch' => 0,
'mfirst' => $mother,
'mlast' => $glname,
'mmatch' => 0);
push(@arSibs, \%curr);
}
}
close LANG_FILE;
sub sortByLast {
$$a{last} cmp $$b{last};
}
my(@ar) = sort(sortByLast @arSibs);
for ($i = 0; $i < @ar; $i++) {
$match = 0;
for ($j = 0; $j < @ar; $j++) {
if ($i == $j) {
next;
}
$fmatch = ($ar[$i]{ffirst} eq $ar[$j]{ffirst}) &&
($ar[$i]{flast} eq $ar[$j]{flast}) &&
($ar[$i]{ffirst} ne "") &&
($ar[$i]{flast} ne "");
$mmatch = ($ar[$i]{mfirst} eq $ar[$j]{mfirst}) &&
($ar[$i]{mlast} eq $ar[$j]{mlast}) &&
($ar[$i]{mfirst} ne "") &&
($ar[$i]{mlast} ne "");;
if ($fmatch || $mmatch) {
if ($match == 0) {
$match = 1;
print $ar[$i]{name} . " " . $ar[$i]{last} . " " . $ar[$i]{agegroup} .
" has conflicts:\n";
}
print "\t" . $ar[$j]{name} . " " . $ar[$j]{last} . " " . $ar[$j]{agegroup};
if ($mmatch) {
print "(" . $ar[$i]{mfirst} . " " . $ar[$i]{mlast} . ")";
}
if ($fmatch) {
print "(" . $ar[$i]{ffirst} . " " . $ar[$i]{flast} . ")";
}
print "\n";
}
}
}
exit(0);
We needed to send an email to every player under the age of 13. All players might not have email addresses or might have multiple ones. I took the League Organizer database and exported all of the players into a comma-separated file (CSV). This also put the column names in the file.
Here is a sample of the file:
fname,lname,mname,street,street2,town,state,zip,...,cust5data,cust6data,fvolunteer,gvolunteer "Alpo","Food","The","199 East","","Tulsa","OK","74133",...,0,0,"",""
The first thing I did was add an '!' on the first line. I did that because I have an old perl script which dynamically matches variable names supplied on the first line (denoted with a '!') with the values of the n'th line. The helper function is:
package read_txtfile_format;
sub main'read_txtfile_format {
local(*file,*format) = @_;
local($first_line, $first_char) = '';
do {
$first_line = <file>;
$first_line =~ /(.)(.*)/;
$first_char = $1;
$first_line = $2;
} until ($first_char eq "!" || eof(file));
if (eof(file)) {
die "There is no ! header line in $file";
}
$format = '$' . join(', $', split(/,/, $first_line));
}
The first pass was to make sure I could read the file and get the fields I wanted:
#! /usr/bin/perl
$, = ' '; # set output field separator
$\ = "\n"; # set output record separator
$FS = "\t";
do 'getthead.pl';
open(LNG_FILE, $ARGV[0]) || die "Can't open LNG_FILE: $!\n";
# Determine the Column Names
do main'read_txtfile_format(*LNG_FILE, *languages); #' Hack to get color correct in vim...
lang: while (<LNG_FILE>) {
next lang if (/^#/ || /^!/);
eval "($languages) = split( /[,\n]/ )";
print "$fname $lname $age |$email|$memail|$femail|";
}
close LANG_FILE;
exit(0);
First we read the input file to get the column names made into variable names. Then each time we process a line, we dynamically assign the different fields to the named variables. I can then use $fname to refer to the first name in that line.
I surround the three different emails with a symbol such that I can detect empty strings:
"FRED" "KIBBLES" 11 |"fred.kibbles@..."||""| "EDITH" "BITS" 14 |""||"edeath.bits"|
The first problem was why was there nothing in the 2nd field? Turns out I used $memail instead of $gemail. I fixed that and got:
"FRED" "KIBBLES" |"fred.kibbles@..."|""|""| "EDITH" "BITS" 14 |""|""|"edeath.bits"|
The next step was to eliminate players who were too old:
if ($age < 13) {
print "$fname $lname $age |$email|$memail|$femail|";
}
And that yields:
"FRED" "KIBBLES" |"fred.kibbles@..."|""|""|
I could have a case where the player had no email addresses to use. I wanted to do that next. I also wanted to only print out the email and sort it. Adding an associative array solves that:
if ($age < 13) {
if ($email ne "") {
$emTo{$email} = $email;
}
if ($gemail ne "") {
$emTo{$gemail} = $gemail;
}
if ($femail ne "") {
$emTo{$femail} = $femail;
}
}
}
foreach $key (sort(keys(%emTo))) {
print "$key";
}
And I got 3 entries per line still. This threw me for a bit, a long while, until I realized that when I saw the output, the '"' were actually input from the files. The comparison was testing for blank email addresses and not ones of the form "". I had to modify the test to:
if ($age < 13) {
if ($email ne "\"\"") {
$emTo{$email} = $email;
}
}
Of course, the output was still of the format:
"fred.kibbles@..."
But this was dead simple to eliminate, I just had to do some regexping:
if ($age < 13) {
if ($email ne "\"\"") {
$emTo{$email} =~ $email = /"(.*)"/;
}
}
I got a parsing error. The =~ was in the wrong part. This should work:
if ($age < 13) {
if ($email ne "\"\"") {
$emTo{$email} = $email =~ /"(.*)"/;
}
}
Nope, still had output of the form:
"fred.kibbles@..."
The problem here is that it didn't matter what I was stuffing into the array, the key was always the orginal string:
$emTo{"fred.kibbles@..."} <- fred.kibbles@...
I'm kinda right, but the value being stored turned out to be 1, not the contents. More on that, the fix for this problem, and making it generic was:
$key = $email =~ /"(.*)"/;
if ($key =~ /@/) {
$emTo{$key} = $key;
}
We can see I'm removing invalid addresses and I can change just one line to make this work for any email field. This should run sweetly. But it didn't. If I added a print debug line:
print "$key";
All I got was:
1 1 1 1
I struggled with this one for a while. I had similar code in another script:
my($base) = /(.*)\.txt/;
This stripped out the base filename from a list passed into the standard input. I'm normally very good about declaring all of my variables before use, but with the dynamic nature of this script, I wasn't doing that. The one thing which stuck out in my mind was that '1' was probably an array subscript. So I finally tried this before I threw up my hands:
($key) = $email =~ /"(.*)"/;
if ($key =~ /@/) {
$emTo{$key} = $key;
}
I.e., I made an array of one element and assigned the results there. This worked!
The final version of the script was:
#! /usr/bin/perl
$, = ' '; # set output field separator
$\ = "\n"; # set output record separator
$FS = "\t";
do 'getthead.pl';
open(LNG_FILE, $ARGV[0]) || die "Can't open LNG_FILE: $!\n";
# Determine the Column Names
do main'read_txtfile_format( *LNG_FILE, *languages ); #' Hack to get color correct in vim...
lang: while (<LNG_FILE>) {
next lang if (/^#/ || /^!/);
eval "($languages) = split( /[,\n]/ )";
if ($age < 13) {
if ($email ne "\"\"") {
($key) = $email =~ /"(.*)"/;
if ($key =~ /@/) {
$emTo{$key} = $key;
}
}
if ($gemail ne "\"\"") {
($key) = $gemail =~ /"(.*)"/;
if ($key =~ /@/) {
$emTo{$key} = $key;
}
}
if ($femail ne "\"\"") {
($key) = $femail =~ /"(.*)"/;
if ($key =~ /@/) {
$emTo{$key} = $key;
}
}
}
}
close LANG_FILE;
foreach $key (sort(keys(%emTo))) {
print "$key";
}
exit(0);
I could tidy it up, make a function to abstract parsing an email address. But why? This works.
It turns out that 364 people will get an email invitation to join the Tulsa Nationals Player Development Program:
[usc@adept ~]$ ./mlist.pl people.txt | wc -l 364
Now to create a mailing list and to make sure I BCC everyone - I don't want to be giving spammers ammunition.
I volunteer with the Union Soccer Club. I do the scheduling of games. I also webmaster. I also fix the computer. It was 3-4 years old and started to suffer. It had Win98 on it and I started to suffer. I had hoped to get through player registration without replacing it, but no, it decided to stop working with the League Organizer database from Logical Solutions.
So I went to CompUSA and got a special. It didn't have a floppy, which the League Organizer uses for backups, so I lent USC my USB floppy. It turned out that the backup was corrupt in some tables, but not in the ones needed to get the competitive teams rostered this week.
I had made a backup earlier, so I wanted to see if it was valid (I can't even really define valid for this product, just whether it produces a given error or not). Only, I do not have a floppy drive on any of my PCs. I do however have one on my Ultra 10. ;>
So, I learned how to load the floppy, snuck the stuff off, and found out that League Organizer expects the backup to be in the root of the drive. It won't read anything in if the data is stored in a subdirectory.
By the way, everyone told me I needed to get Microsoft Office for the computer. My reply was to ask the Office Manager what she used it for - not much and she didn't like it. I'm going to save (each dollar spent is from a kid's registration fees) and get OpenOffice.
I wouldn't expect to see this highly adopted outside of Silicon Valley, but I used Perl this weekend to help me document new rosters for the Fall season. The background is that we have new team formation guidelines and decided to do things by hand. I.e., flip papers over to do random sort by school. I took the lists of teams home and decided to enter them in files. I chose to use file names of u<age><sex>[-<coach>].txt. I also added the player names as I would say them in English, i.e., first and then last name. I noticed that some of the last names had spaces.
I had been reading the sed & awk book from O'Reilly, so my first task (I didn't realize I wanted to do web pages) was to sort the teams by last name and output them as last, then first name.
I tried:
cat $1 | sed "s/\(.*\) \(.*\)/\2, \1/" | sort > sort/$1
There is a problem:
[usc@adept girls]$ more u8g-vanhalen.txt Alley Angle Big Bang Kandy Kanaviral Tailor Raider Monster Skye Haley Van Halen [usc@adept girls]$ cat u8g-vanhalen.txt | sed "s/\(.*\) \(.*\)/\2, \1/" | sort Angle, Alley Bang, Big Halen, Haley Van Kanaviral, Kandy Raider, Tailor Skye, Monster
We want Van Halen and not Haley Van. Also, I put an '*' at the end of some lines to signal a problem. The easiest regular expression I found was to state I did not want a space in the first name:
[usc@adept girls]$ cat u8g-vanhalen.txt | sed "s/\([^ ]*\) \(.*\)/\2, \1/" | sort Angle, Alley Bang, Big Kanaviral, Kandy Raider, Tailor Skye, Monster Van Halen, Haley
Okay, I went to sleep since I could print out the rosters in a nice format. When I woke up, I decided I wanted the rosters to be online and I wanted to link in any problems. E.g., Alley might not have turned in her birth certificate. I didn't want to spend too much time with sed or awk, so I went back to Perl. I understand the allure of commercial software for doing leagues, I'm just glad I know how to do data structures. :)
I'm not going to go over much of the details. I sat there and tweaked the script as I wrote it. The hardest part was remembering how to get an array of structures built and dereferenced. And sorting on age group and then name (for the team page) was frustrating.
No, the thing that really bugs me is how do I get this source code into this blog? I have plenty of metacharacters which might get mangled.
[usc@adept teams]$ cat report.pl | sed "s/</\</g ; s/>/\>/g"
Sweet! I couldn't enter my example as it got mangled!. I had to work to figure out how to get the '&' in there!
I then tested this in a new entry window and made sure it worked.
In the code, you can see where I finally started remembering some of the Perl I had forgotten (or finally made sense of some old scripts I had). I did more pattern matching than I normally do.
The other thing which confused me was the statement:
my($first, $last) = $t =~ /$nameRegEx/;
I kept on thinking it would mangle the contents of $t, but I was getting the precendence wrong. This reads as make a copy of the value of $t, apply the pattern match against that copy, and return the results into $first and $last. It may not be described that way in any of the literature, but it is a way to think of it.
Anyway, here is the script, which is totally useless to anyone other than myself. Except if you want to scavange concepts from it.
#!/usr/bin/perl
#
# Use:
#
# [usc@adept girls]$ ls -1 *.txt | ../report.pl
#
$, = ' '; # set output field separator
$\ = "\n"; # set output record separator
$| = 1;
# Process the files.
my($nameRegEx) = "([^ ]*) (.*)";
#
# Get the list of coaches
#
# File format is first and then last name.
#
open(FP, "<../coaches.txt") || die "Could not read coaches: $!";
while (<FP>) {
chomp();
my($first, $last) = /$nameRegEx/;
my($id) = join("", split(/ /, $last));
$id =~ tr/A-Z/a-z/;
$coaches{$id} = "$first $last";
# print "$last, $first - $id";
}
close(FP);
my(@arProblems);
#
# Get the list of problems
#
# Fields are ':' separated.
#
open(FP, "<../problems.txt") || die "Could not read problems: $!";
while (<FP>) {
chomp();
my($name, $age, $reason) = split(/:/);
my($first, $last) = $name =~ /$nameRegEx/;
if ($reason =~ /BC/) {
$reason = "Needs copy of birth certificate";
} elsif ($reason =~ /PU/) {
$reason = "Needs to fill out Play Up Form";
}
my(%curr) = ('first' => $first,
'last' => $last,
'age' => $age,
'reason' => $reason);
push(@arProblems, \%curr);
}
close(FP);
#my(@ar) = @arProblems;
#for ($j = 0; $j < @ar; $j++) {
# print join(" ", $ar[$j]{first}, $ar[$j]{last}, $ar[$j]{age}, $ar[$j]{reason});
#}
open(TEAMLIST, ">html/index.html") || die "Could not write html/$base.html: $!";
print TEAMLIST << "EndOfTeamHeader";
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<title>USC Fall 2006 Teams</title>
</head>
<body>
<h1>Teams</h1>
<ol>
EndOfTeamHeader
#foreach $key (sort(keys(%coaches))) {
# print $coaches{$key} . " $key";
#}
my(@arTeams);
while (<>) {
chomp();
my($file) = $_;
my($base) = /(.*)\.txt/;
my($i, $key, $coachDisplay, $age);
my(%players);
open(FP, ">html/$base.html") || die "Could not write html/$base.html: $!";
open(TEAM, "<$file") || die "Could not read $file: $!";
print FP << "EndOfHeader";
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<title> Team $base</title>
</head>
<body>
EndOfHeader
my (%curr) = ('file' => $base,
'count' => 0,
'gender' => "",
'age' => 0,
'display' => "",
'first' => "",
'last' => "",
'error' => 0,
'errorStr' => "");
if (/-/) {
my($coachKey);
($age, $coachKey) = /(.*)-(.*)\.txt/;
$age =~ tr/a-z/A-Z/;
if ($coaches{$coachKey}) {
my($t) = $coaches{$coachKey};
my($first, $last) = $t =~ /$nameRegEx/;
$curr{first} = $first;
$curr{last} = $last;
print "$file - the coach is $first $last ";
print FP "<h1>$age - $coaches{$coachKey}</h1>";
$curr{display} = $coaches{$coachKey};
if (!($first cmp "The")) {
$curr{error} = 1;
$curr{errorStr} = "Need first name!";
}
} else {
print "$file - has no matching coach for $coachKey";
print FP "<h1>$age - $coachKey <font color=\"red\">No matching Coach entry!</font></h1>";
$curr{display} = $coachKey;
$curr{error} = 1;
$curr{errorStr} = "No matching Coach entry!";
}
} else {
$age = $base;
$age =~ tr/a-z/A-Z/;
print "$file - has no coach!";
print FP "<h1>$age - <font color=\"red\">Coach to be assigned</font></h1>";
$curr{display} = $age;
$curr{error} = 1;
$curr{errorStr} = "Coach to be assigned!";
}
my($ageGroup, $gender) = $age =~ /U(\d*)(.*)/;
$curr{age} = $ageGroup;
$curr{gender} = $gender;
print FP "\n\n";
while (<TEAM>) {
chomp();
$key = $_;
$key =~ s/([^ ]*) (.*)/\2, \1/g;
# print "Adding $key";
$players{$key} = $_;
}
$i = 0;
print FP "<ol>";
foreach $key (sort(keys(%players))) {
my($first, $last) = $players{$key} =~ /$nameRegEx/;
my($problemStr) = "";
#
# Search for a problem
#
for ($j = 0; $j < @arProblems; $j++) {
if (($first eq $arProblems[$j]{first})
&& ($last eq $arProblems[$j]{last})) {
$problemStr = " <font color=\"red\">" . $arProblems[$j]{reason} . "</font>";
print "XXX $first $last $problemStr";
} else {
# print "YYY $first $last no match on " . join(" ",
# $arProblems[$j]{first}, $arProblems[$j]{last},
# $arProblems[$j]{age}, $arProblems[$j]{reason});
}
}
if ($key =~ /\*/) {
print FP "<li>$key $problemStr <font color=\"red\">Not yet added to team, reserving spot.</font></li>";
} else {
print FP "<li>$key $problemStr </li>";
}
$i++;
}
print FP "</ol>";
print FP << "EndOfFooter";
</body>
</html>
EndOfFooter
close(TEAM);
close(FP);
$curr{count} = $i;
push(@arTeams , \%curr);
undef(%players);
}
#sub sortByAgeThenLast {
# if ($$b{age} < $$a{age}) {
# -1;
# } elsif ($$b{age} > $$a{age}) {
# +1
# } else {
# $$a{last} cmp $$b{last};
# }
#}
sub sortByAgeThenLast {
# print $$a{age} . " vs " . $$b{age} . " and (" . $$a{last} . " vs " . $$b{last} . ")";
if ($$b{age} < $$a{age}) {
+1;
} elsif ($$b{age} > $$a{age}) {
-1;
} else {
$$a{last} cmp $$b{last};
}
}
my(@ar) = sort(sortByAgeThenLast @arTeams);
for ($i = 0; $i < @ar; $i++) {
if ($ar[$i]{error}) {
print TEAMLIST "<li><a href=\"" . $ar[$i]{file} . ".html\"> U"
. $ar[$i]{age} . $ar[$i]{gender} . " - " . $ar[$i]{display}
. "</a> Players: " . $ar[$i]{count} . " - <font color=\"red\">"
. $ar[$i]{errorStr} . "</font></li>";
} else {
print TEAMLIST "<li><a href=\"" . $ar[$i]{file} . ".html\"> U"
. $ar[$i]{age} . $ar[$i]{gender} . " - " . $ar[$i]{display}
. "</a> Players: " . $ar[$i]{count} . "</li>";
}
}
print FP << "EndOfTeamFooter";
</ol>
</body>
</html>
EndOfTeamFooter
close(TEAMLIST);
exit(0);
print join(" ", $ar[$i]{file},
$ar[$i]{count},
$ar[$i]{age},
$ar[$i]{display},
$ar[$i]{first},
$ar[$i]{last},
$ar[$i]{error},
$ar[$i]{errorStr});
Without Baseball coaching to fill my evenings, I've been looking at making the jersey's for the Phantoms in the Fall 2006 season. In the past, we've either gone for quality (trying to look competitive) or ulitarian (getting tired of new gear). We used to have a light blue and white basis to the uniforms. But last fall, I took us to a red and black stripe pattern - think AC Milan. What I saw in the ads was this:
I would have prefered a narrower stripe. But the real killer was the service offered by the provider and the fact that the black did not extend to the back. We had 4 teams pick red and black for their colors. If he black had gone to the back, we wouldn't have had to wear targets for half of our games.
So last Spring, we started wearing purple and black. I happen to like purple first and then orange as far as colors go. We already had an orange team in the league.
We paid $18 for the jerseys and they held up better than I thought they would. But then again, this was only for 1/2 the year. (The Ajax jerseys actually have held up the best - the material is of high quality.
For the 3v3 tournament, we designed our own jerseys - half the kids had not played for me in the Spring. I blogged about the jerseys and how I lost out on the design battle. We ended up looking like this:
We achieved the look by spending $2.99 per kid shirt at Hobby-Lobby. We also bought some tumble dry Desert Purple and Orange spray bottles for $1.99 each. Note that you really have to check prices, at one of the stores, the bottles were priced at $4.99. The bottles were made by Sei:
I was happy with the result, but wanted to go to the original design that I had proposed. So I went back and got 3 child shirts and 1 adult shirt. I wanted to experiment.
The first result was a painted version of the ajax style. I laid the shirt out on a ladder and sprayed on first the puple and then the orange. I wasn't to happy as I was afraid the dye might seep through or run. The purple tended to splotch more, i.e., solid clumps would come out.
You can see a lot of white, especially where I wasn't willing to see the colors mix. It isn't shown here, but the back stripe of orange is bigger and dominates. Because of the other team in the league, I'd prefer the purple dominate.
The second try was with rubber bands to seperate the striped areas. It really looks bad:
Evidently the back was on top and got the majority of the spray. Notice how my earlier fear of leaking through a layer is misfounded. I think this would have turned out better if I had been dipping it. Also, I really did not want the white to show as much. My wife likes the more traditional tye dye look here, but I guess I want a more finished product.
The next look was another paint scheme, based off of the 2004 Rangers' strips:
You can see there is much less white in this version. I took a long box (about 3 ft), with 8 in width and 8 in depth. I pulled the shirt over it and was not as concerned about leakage. You can see in the second picture that I needn't have worried that much. The underside is pretty white.
Both the Monster and one of his soccer buddies like this version. The organge is there, but not dominant.
Finally, for the adult shirt, I went back to a tye dye approach - this time I made sure to soak the thing with as much dye as possible. When I emptied a bottle, I would pour the remaining product onto the shirt. You can see the deeper purple splotches on it:
Also, I tied off the sleeves and made them orange. I found that with more material, it was easier to dig into the wad and get more coverage. But when I pulled the rubber bands off, I found that the back was once again on top and the front suffered for it. You can see the orange really provides minimal coverage on the front versus the back. I had run out of orange dye, but I took the remaining purple dye and spray painted a bit. You can see it in the detailed version (side shot). Look at the eveness on the upper front versus the chaotic lower front.
With this Sei product, you dye the material and let it air dry. Once it has done that, you heat dry it, either with a hair dryer or a drying machine.
I'd like a more even distribution out of the pump. I'd also like bigger quantity bottles. I'd probably mix up my own dye solution and use a pump from Lowes. I don't have anything against the Sei product, I just want to get more dye on the material - to get it to seep in.
Of the approaches, I'd pick the painted approach over the tye-dyed look. With the box, I think either the ajax or striped style would be easy to do, i.e., I could eliminate the white between the colors in the ajax.
All in all, it was a fun way to spend a few evenings - and my son got a new wardrobe for both the pool and practice.
France sure does not look like the team picked to bomb out of pool play. I saw the second half and they looked awesome. I was hoping the team to come out of this match, whether France or Spain, would be able to give Brazil a challenge.
Right now, it looks like France are a team to be concerned with in the WC.
I thought Zidane was offside on that last play. It might have been the camera angle, because no one seemed upset on the Spanish side.
But, with 2 attackers and 3 defenders, that made it all the more a thing of beauty. For me, the Beasly pass to Dempsey, the Beckham goal, this goal, and Crespo's backheel back to Cambiasso have been the positive highlights of the WC.
The negative highlights have to be the diving and FIFA's setting up the referees to fail.
I'm hoping to see more positive highlights.
My earliest memory of David Beckham was the foul he committed versus Diego Simeone of Argentina in the 1998 World Cup. I wasn't too impressed. I next saw him four years later and again, I wasn't impressed. You have to understand the coverage afforded football in the US to realize why I never saw his feats with Manchester United. It wasn't until Fox Soccer became a staple that you could get anything. But, you have to travel way up the dial, which means only die-hards will find it. For example, outside of his family, I'm in the minority in the US who watched Manchester United games for a chance to see Jonathan Spector play a couple of years ago.
I thought Beckham was getting mauled by Marcelo Balboa the entire game. He was nonstop about how slow Beckham was, how much of a liability he was, and how everyone knew the ball was going down the other side to Joe Cole to quickly feed Rooney. When Beckham scored, Balboa gave lip service to the only reason Beckham was in the game at all was his service off of set pieces.
I then saw a highlight reel today on ESPN with Stuart Scott talking about Beckham. If you hadn't seen the game, you would have thought it was Beckham finally touches the ball on the set piece, scores, throws up, and gets pulled off. I saw Beckham heave and then I saw him still haul his butt all over the field. I distinctly recall him on the defensive half breaking up plays by kicking the ball out for a thowin.
To say I'm disappointed with the on-air coverage by ESPN is an understatement. All year, you get your fix on the Fox Soccer Channel and when the UEFA and Champion's League Cups come about, you have to struggle to find the matches because they are on ESPN2. And now the coverage starts 5 minutes before the games and ends about 5 minutes after. You get to see Brent Musburger blow the canned questions - when the segment is advertised before the commercials as "Rooney to play 90?", how surprising is it to see him try to be smooth about whether Wayne Rooney is going to play the full 90?
I like Eric Wynalda and John Harkes for their analysis. And I love the shear sass that Julie Foudy brings to the mix. But, I hate the surgical precision that the network brings to inserting the World Cup into the mix.
I also hate that FSC is not allowed to show more than about 15 seconds of highlights. I like their analysis and opinion. I like being able to get commentary from Sky Sports and other English coverage. I even put up with Rugby and Cricket highlights.
I also like the coverage EPSN and Soccernet provide online. Many of the articles online are just taken from the wire, i.e., no matter what site you go to, you get the same content. I like the way Soccernet is packaged.
Back to Beckham, I saw some fan reactions later, probably on FSC, and they indicated that he is probably also being roasted by the European press for not being in shape. That also ties in with a recent article where Beckham states he would sit if Sven-Goran Eriksson asked him to do so (it followed an article where Sven claimed no one was safe, not even the captain). And postgame coverage of the match does seem to focus on whether Beckham is the weakest link or not.
His goal was a thing of beauty. He hurled. He covered the field. What more do you want?
I'm guessing this picture is of the 1952-53 Annbank United F.C. squad who were runners up in the Scottish Junior Cup. My grandfather is on the right, he is the manager/coach with the cap on. I haven't a clue as to who anyone else is in the picture. My aunts were children when this was taken and you would have had to have been a relative for it to stick in their minds.
Team Names: top left to right.
Front
I only knew him after he had retired from the coal mines (or close enough that it did not matter). When I played with a football, it was always however I wanted to do it. He never came out to coach me. It was a different era back then, when kids played outside and adults were not as fanatical about organized sports. It was the same for me when I was in the US or Germany.
I was back in Annbank a couple of years ago, it hasn't changed that much - the houses now have driveways in the front and solariums in the back. Poli still comes by every night with ice cream. The kids still play out on the street. You might have a neighbor kid or two in your yard. I found it really strange and disturbing. Instead of thinking, "Ahh, the good old days!", I was wondering if these people cared about their children. Times were not simpler, they had just not been robbed of their neighborhoods. Violence is much more a part of life out there.
But back to my grandfather - in a way, once it really clicked that he was a coach, I was sad he never got a chance to help me. I don't think it would have changed my time as a player that much (I've always been tall and was coordinationally challenged). But as an adult coach, I wish he had words of coaching wisdom to pass down to me. As it is, the best I have is a fierce love of Rangers.