Monday, December 29, 2014

MATLAB Script: Dealing Cards

I played cards (poorly) over Christmas, and thought a good way to break through the end-of-year boredom at work would be to write a quick MATLAB script that dealt a given number of cards from a randomly shuffled deck to a given number of players.

I got to use the eval command again, which is always exciting.



% Randomly shuffle a deck of cards
% Distribute to a given number of players (user input)
% Display the hands for each player

% Assign a number for each card
% 1-13 are for Spades (in order)
% 14-26 are for Clubs
% 27-39 are for Diamonds
% 40-52 are for Hearts
card_names{1} = '2 of Spades';
card_names{2} = '3 of Spades';
card_names{3} = '4 of Spades';
card_names{4} = '5 of Spades';
card_names{5} = '6 of Spades';
card_names{6} = '7 of Spades';
card_names{7} = '8 of Spades';
card_names{8} = '9 of Spades';
card_names{9} = '10 of Spades';
card_names{10} = 'Jack of Spades';
card_names{11} = 'Queen of Spades';
card_names{12} = 'King of Spades';
card_names{13} = 'Ace of Spades';
card_names{14} = '2 of Clubs';
card_names{15} = '3 of Clubs';
card_names{16} = '4 of Clubs';
card_names{17} = '5 of Clubs';
card_names{18} = '6 of Clubs';
card_names{19} = '7 of Clubs';
card_names{20} = '8 of Clubs';
card_names{21} = '9 of Clubs';
card_names{22} = '10 of Clubs';
card_names{23} = 'Jack of Clubs';
card_names{24} = 'Queen of Clubs';
card_names{25} = 'King of Clubs';
card_names{26} = 'Ace of Clubs';
card_names{27} = '2 of Diamonds';
card_names{28} = '3 of Diamonds';
card_names{29} = '4 of Diamonds';
card_names{30} = '5 of Diamonds';
card_names{31} = '6 of Diamonds';
card_names{32} = '7 of Diamonds';
card_names{33} = '8 of Diamonds';
card_names{34} = '9 of Diamonds';
card_names{35} = '10 of Diamonds';
card_names{36} = 'Jack of Diamonds';
card_names{37} = 'Queen of Diamonds';
card_names{38} = 'King of Diamonds';
card_names{39} = 'Ace of Diamonds';
card_names{40} = '2 of Hearts';
card_names{41} = '3 of Hearts';
card_names{42} = '4 of Hearts';
card_names{43} = '5 of Hearts';
card_names{44} = '6 of Hearts';
card_names{45} = '7 of Hearts';
card_names{46} = '8 of Hearts';
card_names{47} = '9 of Hearts';
card_names{48} = '10 of Hearts';
card_names{49} = 'Jack of Hearts';
card_names{50} = 'Queen of Hearts';
card_names{51} = 'King of Hearts';
card_names{52} = 'Ace of Hearts';
% Shuffle the deck
shuffled_deck = randperm(52);
% Ask the user for # of players and # of cards per player
clc
num_players = input('How many players? Enter here: ');
num_cards = input('How many cards per player? Enter here: ');
disp('----------------------')
% Create empty player hand arrays
for k=1:1:num_players
  plyr_hnd_cmd_str = ['player_hand_',num2str(k), '= [];'];
  eval(plyr_hnd_cmd_str);
end
% Deal cards out to the players
total_cards_to_deal = num_players * num_cards;
i=1;
while i < total_cards_to_deal
    for j=1:1:num_players
        deal_card_cmd_str = ['player_hand_', num2str(j), ' = [player_hand_',num2str(j), ', shuffled_deck(i)];'];
        eval(deal_card_cmd_str);
        i = i+1;
    end    
end
% Display each player's hand
for k=1:1:num_players
    disp_plyr_str = ['Player ', num2str(k), '''s hand: '];
    disp(disp_plyr_str)
    for m=1:1:num_cards
        card_name_cmd_str = ['card_name_str = card_names{player_hand_', num2str(k), '(m)};'];
        eval(card_name_cmd_str)
        disp(card_name_str)
    end
    disp('----------------------')
end

Friday, December 19, 2014

MATLAB Script: Best MBA Course

Upon the completion of my MBA at BU, I wanted to figure out what my top 5 courses (out of 18) were.

So I wrote a brute-force, unsexy MATLAB script that did a round-robin style side-by-side comparison.

It worked out fairly well. I even put in a very rudimentary tiebreaker loop wherein the user is asked to break the tie. Basically a "head to head" comparison, which, upon further review, should be pre-determined, since the user had already done a head-to-head comparison of the two courses before. Room for improvement.

Here it is:



% Determines the ranking of best MBA courses taken
% The user picks the best course by choosing between all combinations
clc
clear
num_courses = 18;
% Create the list of courses
% Course 1: "OB 712: Managing Organizations"
course_names{1} = 'OB 712: Managing Organizations';
% Course 2: "AC 711: Accounting"
course_names{2} = 'AC 711: Accounting';
% Course 3: "MK 724: Marketing Management"
course_names{3} = 'MK 724: Marketing Management';
% Course 4: "QM 717: Statistics"
course_names{4} = 'QM 717: Statistics';
% Course 5: "FE 730: Economics"
course_names{5} = 'FE 730: Economics';
% Course 6: "IS 711: IT Strategies"
course_names{6} = 'IS 711: IT Strategies';
% Course 7: "OM 726: Operations"
course_names{7} = 'OM 726: Operations';
% Course 8: "SI 751: Strategy"
course_names{8} = 'SI 751: Strategy';
% Course 9: "PL 700: Law and Ethics"
course_names{9} = 'PL 700: Law and Ethics';
% Course 10: "ES 700: Executive Presentation"
course_names{10} = 'ES 700: Executive Presentation';
% Course 11: "OB 853: Negotiations"
course_names{11} = 'OB 853: Negotiations';
% Course 12: "OM 880: Product Design"
course_names{12} = 'OM 880: Product Design';
% Course 13: "MK 864: Pricing"
course_names{13} = 'MK 864: Pricing';
% Course 14: "MK 862: High-Tech Marketing"
course_names{14} = 'MK 862: High-Tech Marketing';
% Course 15: "IS 827: Platforms"
course_names{15} = 'IS 827: Platforms';
% Course 16: "OB 848: Leadership"
course_names{16} = 'OB 848: Leadership';
% Course 17: "MK 852: Marketing Analytics"
course_names{17} = 'MK 852: Marketing Analytics';
% Course 18: "SI 845: Technology Strategy"
course_names{18} = 'SI 845: Technology Strategy';
% ES 700 and FE 722 were not considered for ranking
% Create a 2x18 matrix of randomly determined combinations
rand_course_combs = rand_combs(num_courses);
%rand_course_combs = rand_combs2(num_courses); %Alternative algorithm that doesn't use Statistics Toolbox
len_rand_course_combs = length(rand_course_combs);
points_array = zeros(1,num_courses);
better_course = 0;
for i=1:1:len_rand_course_combs
    course_1_name = char(course_names(rand_course_combs(i,1)));
    course_2_name = char(course_names(rand_course_combs(i,2)));
    prompt_str = [course_1_name, ' (1) vs. ',course_2_name, ' (2): '];
    clc
    pct_cmplt = 100*((i-1)/len_rand_course_combs);
    pct_cmplt_str = [num2str(pct_cmplt), '% complete'];
    disp(pct_cmplt_str)
    disp('Which one was better?');
    better_course = input(prompt_str);
       if better_course == 1
           points_array(rand_course_combs(i,1)) = points_array(rand_course_combs(i,1)) + 1;
       elseif better_course == 2
           points_array(rand_course_combs(i,2)) = points_array(rand_course_combs(i,2)) + 1;
       end
end
% Sort, rank, and display
sorted_points_array = sort(points_array,'descend');
len_points_array = length(points_array);
rank_array = zeros(1,len_points_array);
i=1;
while i <= len_points_array
   rank_array_temp = find(points_array == sorted_points_array(i));
   if length(rank_array_temp) > 1
       % Assumes only two values will be "tied". Should probably make this
       % more robust.
       % Break the tie by prompting user to pick the better option
       disp('Found a tie!')
       disp('Need to break it!')
       disp('Which one was better?')
       course_1_name = course_names{rank_array_temp(1)};
       course_2_name = course_names{rank_array_temp(2)};
       prompt_str = [course_1_name, ' (1) vs. ',course_2_name, ' (2): '];
       tiebreaker_choice = input(prompt_str);
       if tiebreaker_choice == 1
      rank_array(i) = rank_array_temp(1);
      rank_array(i+1) = rank_array_temp(2);
       else
        rank_array(i) = rank_array_temp(2);
        rank_array(i+1) = rank_array_temp(1);
       end
       i=i+2;
   else
   rank_array(i) = find(points_array == sorted_points_array(i));
   i=i+1;
   end
end
len_rank_array = length(rank_array);
for j = 1:1:len_rank_array
    clc
   disp_str = [num2str(j), ': ',course_names{rank_array(j)}, ' : ',num2str(sorted_points_array(j)), ' points.']; 
    disp(disp_str)
end



Here were the results:

1: OM 880: Product Design : 17 points.
2: IS 827: Platforms : 16 points.
3: OB 853: Negotiations : 15 points.
4: MK 862: High-Tech Marketing : 14 points.
5: MK 852: Marketing Analytics : 13 points.
6: MK 724: Marketing Management : 12 points.
7: SI 751: Strategy : 11 points.
8: FE 730: Economics : 10 points.
9: AC 711: Accounting : 9 points.
10: SI 845: Technology Strategy : 8 points.
11: OB 712: Managing Organizations : 7 points.
12: MK 864: Pricing : 6 points.
13: ES 700: Executive Presentation : 4 points.
14: OM 726: Operations : 4 points.
15: OB 848: Leadership : 3 points.
16: IS 711: IT Strategies : 2 points.
17: QM 717: Statistics : 2 points.
18: PL 700: Law and Ethics : 0 points.

Thursday, July 3, 2014

Improved Script

I decided to get a little fancy with the "random PDF" script from my last post.

I modified the random selection element to be weighted based on age. I decided to start simple by simply dividing file age (in days) by 10 (I'll try using the log next), rounding up (using math.ceil) and randomly selecting from a new array (called "weighted_files") which gives more weight to older files. Basically: I want it to be more likely that an older file is opened rather than a newer file. My reading model has now shifted to a FIFO scheme.

Here it is, with a few comments.


#! /usr/bin/python
import time, os, math, sys, shutil
from random import randint
clear = lambda: os.system('cls')
clear()
path = '[DIRECTORY WITH PDF FILES IN IT]'

pdf_files = [f for f in os.listdir(path) if f.endswith('.pdf')]

len_pdf_files = len(pdf_files)
if len_pdf_files == 0:
print ("No PDF files found!")
time.sleep(3)
sys.exit(0)

pdf_ages = []

for pdf_file in pdf_files:
os.rename(os.path.join(path, pdf_file), os.path.join(path, pdf_file.replace(' ', '_')))
file_loc = path + '/' + pdf_file
file_age_sec = time.time() - os.path.getmtime(file_loc)
file_age_days = file_age_sec/60/60/24
file_age_days_round = math.floor(file_age_days)
pdf_ages.append(file_age_days_round)
# Build an array for use in weighted random selection
weighted_files = []
weighting_factor = 10 # arbitrary
for index, pdf_age in enumerate(pdf_ages):
weight = math.ceil(pdf_age/weighting_factor)

# Adds the index number (from pdf_ages, which should match pdf_files) in proportion to the weight
for i in range(0,weight):
weighted_files.append(index) 

len_weighted_files = len(weighted_files)
rand_int = randint(0,len_weighted_files-1)
pdf_files_index = weighted_files[rand_int]
file_name_str = str(pdf_files[pdf_files_index])

file_loc_str = path + '/' + file_name_str
pdf_cmd_str = "start " + file_loc_str
os.system(pdf_cmd_str)
valid_command = 0
while valid_command == 0 :
delete_file = input("Delete file? Y or N: ")
if (delete_file == "Y" or delete_file == "y"):
os.remove(file_loc_str)
print (file_name_str + " has been deleted! Good work!")
new_num_pdfs = len_pdf_files - 1
new_num_pdfs_str = str(new_num_pdfs)
print ("There are now ",new_num_pdfs_str," PDFs remaining.")
time.sleep(3)
valid_command = 1
sys.exit(0)
elif (delete_file == "N" or delete_file == "n"):
print ("Fine then, but next time, delete it!")
dest_folder_loc_str = 'D:/Users/a27m8mt/Documents/Things_To_Read/Hold_for_later/'
shutil.move(file_loc_str,dest_folder_loc_str)
print (file_name_str + " has been moved to the holding folder: " + dest_folder_loc_str)
time.sleep(3)
valid_command = 1
sys.exit(0)
else:
print ("Invalid command. Try again.")

Tuesday, July 1, 2014

Python Script for Opening a Random PDF in a Given Directory

I decided to try learning Python, so I came up with a script of nominal utility. It's probably quite ugly, but it works.

It opens a random PDF from a directory full of PDFs that I've accumulated over the past year or so of articles I've been meaning to read. At the end, it prompts the user to save or delete the file. It's quite cathartic.

If you have a similar problem, I hope this helps clean up your life a bit. Red text is where you fill in the blanks. If you're using Windows, make sure to use forward slashes for the path (e.g. 'D:/users/something/something')


import os, sys, time
from random import randint
# Clear the command window
clear = lambda: os.system('cls')
clear()
# Set the path with all of the PDF files
path = '[directory with all of the files]'
# Create a list of all of the PDF files
pdf_files = [f for f in os.listdir(path) if f.endswith('.pdf')]
# Clean up the names by removing spaces
for pdf_file in pdf_files:
os.rename(os.path.join(path, pdf_file), os.path.join(path, pdf_file.replace(' ', '_')))
len_pdf_files = len(pdf_files)
# Generate a random number
rand_int = randint(0,len_pdf_files)
# Get the name of a random PDF file
file_name_str = str(pdf_files[rand_int])
file_loc_str = path + '/' + file_name_str
# Generate a string to be used to open the file
pdf_cmd_str = "start " + file_loc_str # Windows uses "start"
# Open the file
os.system(pdf_cmd_str)
# Prompt the user to delete the file
delete_file = input("Delete file? Y or N: ")
if delete_file == "Y":
os.remove(file_loc_str)
print (file_name_str + " deleted! Good work!")
# Give the user a few seconds to read the statement
time.sleep(3)
sys.exit(0)
else:
# Chastise them for being a hoarder, while enabling them
print ("Fine then, but next time, delete it!")
time.sleep(3)
sys.exit(0)