Turning an entire script into a function

22 views (last 30 days)
Alex Skantz
Alex Skantz on 14 Oct 2021
Edited: DGM on 17 Oct 2021
Please help!! I've been trying to functionize my script all day. I followed all the instructions (however my script varies slightly from the examples used in class because these assignments need to be unique) and I can't figure it out. I need to turn my entire battlehsip game into a finction. I keep getting "error" messages whenever I run the script but It doesn't tell me WHAT the error is (no invalid syntax or improper use message or anything. just "error".)
Here is my battleship game script working porperly before I tried to turn it into a function.
clear all
close all
clc
rowdim=input('give me the number of rows on the board: ');
coldim=input('give me the number of columns on the board: ');
GameBoard=zeros(rowdim,coldim); %defined board dimensions
%matlab puts a random ship on the board
N=3; %defines N as number of ships on gameboard
battleship_row=randi(rowdim,[1 N]); %random row
battleship_col=randi(coldim,[1 N]); %random column
GameBoard(battleship_row,battleship_col)=0;%%'0' hides the loaction of the battleship (unless it is correct)
%playguess
for guessnum=1:5
rowguess=input('guess what row the battleship is in: ');
colguess=input('guess what column the battleship is in: ');
[winloss]=results(rowguess,colguess,battleship_row,battleship_col);
%assesses the results
if winloss == 1
GameBoard(rowguess,colguess)=3; %marks winning guess with '3' on board
break; %finishes the loop, because you won
GameBoard %displays the winning gameboard
else
disp('try again')
GameBoard(rowguess,colguess)=2; %mark loss with '2' on board if guess is incorrect
GameBoard %displays the gameboard to visualize your loss
end
end
[winloss]=results(rowguess,colguess,battleship_row,battleship_col); %calling subplot
GameBoard
%subfunction from here on
function [winloss]=results(rowguess,colguess,battleship_row,battleship_col)
if any((rowguess == battleship_row) & (colguess == battleship_col));
winloss=1;
disp ('You sunk the battleship! You won!');
else
winloss=0;
disp(' ');
end
end
How can I go about turning the WHOLE script into a function (with the subfcuntions included as nested functions?)
  6 Comments
the cyclist
the cyclist on 16 Oct 2021
The correct syntax for subfunctions inside function is NOT ...
function function_name()
function subfunction_name()
end % end of subfunction
end % End of main function
The correct syntax is ...
function function_name()
end % End of main function
function subfunction_name()
end % end of subfunction
% and then add more subfunctions after that

Sign in to comment.

Answers (2)

the cyclist
the cyclist on 14 Oct 2021
Edited: the cyclist on 14 Oct 2021
Put this code into a file named battleship.m
function [] = battleship()
clear all
close all
clc
rowdim=input('give me the number of rows on the board: ');
coldim=input('give me the number of columns on the board: ');
GameBoard=zeros(rowdim,coldim); %defined board dimensions
%matlab puts a random ship on the board
N=3; %defines N as number of ships on gameboard
battleship_row=randi(rowdim,[1 N]); %random row
battleship_col=randi(coldim,[1 N]); %random column
GameBoard(battleship_row,battleship_col)=0;%%'0' hides the loaction of the battleship (unless it is correct)
%playguess
for guessnum=1:5
rowguess=input('guess what row the battleship is in: ');
colguess=input('guess what column the battleship is in: ');
[winloss]=results(rowguess,colguess,battleship_row,battleship_col);
%assesses the results
if winloss == 1
GameBoard(rowguess,colguess)=3; %marks winning guess with '3' on board
break; %finishes the loop, because you won
GameBoard %displays the winning gameboard
else
disp('try again')
GameBoard(rowguess,colguess)=2; %mark loss with '2' on board if guess is incorrect
GameBoard %displays the gameboard to visualize your loss
end
end
[winloss]=results(rowguess,colguess,battleship_row,battleship_col); %calling subplot
GameBoard
end
%subfunction from here on
function [winloss]=results(rowguess,colguess,battleship_row,battleship_col)
if any((rowguess == battleship_row) & (colguess == battleship_col));
winloss=1;
disp ('You sunk the battleship! You won!');
else
winloss=0;
disp(' ');
end
end

DGM
DGM on 15 Oct 2021
Edited: DGM on 15 Oct 2021
Try something like this.
function [playerwon] = Battleship3(boardsize,numofguess,numships)
% playerwon = Battleship3({boardsize},{numberofguesses},{numberofships})
% A simple battleship game. This would be a description of some sort.
%
% Optional arguments include
% BOARDSIZE specifies the size of the playing area (default [10 10])
% This is a 2-element vector specifying [rows columns].
% NUMBEROFGUESSES specifies the number of attempts the user has
% before the session terminates. (default 10)
% NUMBEROFSHIPS specifies the number of battleships on the board
% (default 5)
%
% Output argument PLAYERWON is a logical scalar
%
% Examples:
% Battleship3(); % start a new game with default settings
% Battleship3([20 20],10,10); % start with a larger board and more ships
%
% Battleship v.02
% programmed by Alex Skantz 10/13/21
% https://www.mathworks.com/matlabcentral/answers/1564041-turning-an-entire-script-into-a-function
%%%%%%SCRIPT TO FUNCTION%%%%%%%%%%
% specify defaults if you want
% there are more efficient ways of doing this, but that doesn't matter here.
if ~exist('boardsize','var')
boardsize = [10 10];
end
if ~exist('numofguess','var')
numofguess = 10;
end
if ~exist('numships','var')
numships = 5;
end
% prepare console
clc
%making the board
GameBoard = zeros(boardsize);
%matlab puts a random ship on the board
battleship_row = randi(boardsize(1),[1 numships]) %random row
battleship_col = randi(boardsize(2),[1 numships]) %random column
%playguess
numhits = 0;
for guessInd = 1:numofguess
rowguess = input('guess what row the battleship is in: ');
colguess = input('guess what column the battleship is in: ');
% you might want to do something to deal with out-of-bounds selections
rowguess = min(max(round(rowguess),1),boardsize(1));
colguess = min(max(round(colguess),1),boardsize(2));
%assesses the results
hitaship = any((rowguess == battleship_row) & (colguess == battleship_col));
if hitaship
numhits = numhits+1;
GameBoard(rowguess,colguess) = 3; %marks winning guess with '3' on board
GameBoard %displays the winning gameboard
playerwon = numhits == numships;
if playerwon
fprintf('\nYou sunk all the battleships! You won!\n')
break; %finishes the loop, because you won
else
fprintf('\nYou sunk a battleship! Try to find the rest!\n')
end
else
GameBoard(rowguess,colguess)=2; %mark loss with '2' on board if guess is incorrect
GameBoard %displays the gameboard to visualize your loss
fprintf('\ntry again\n')
end
end
end %of the entire file/function
Things to note:
The test used in the nested function outputs a logical, so the conditional structure isn't really necessary for assigning the value to a flag. The other things that were done in the conditional structure can just be moved into the calling code.
It's possible for the player to enter invalid coordinates. This could either cause unintended expansion of the game board (if the index is larger than the board), or it could cause an indexing error (if the index is not a positive integer). In this example, I've chosen to address this in a very crude fashion by simply clamping the indices to stay within the board. Another approach may be to discard the point and re-prompt the user until a valid point is entered.
The inclusion of defaults is possibly outside of your requirements, but it's probably a good idea in practice. That said, using varargin instead of a fixed argument list is generally favorable. It's marginally faster than using exist(), and it allows the user flexibility in how they want to specify the arguments. Again, it's probably all that much further beyond your requirements.
Note the synopsis. This block of commented text is what's returned when you call help on your function. My example is sloppy, but this is the primary information a user has to be able to use the function. Describe the function, its purpose, its inputs and outputs. Describe defaults (if any), and if appropriate, provide simple examples or links to related functions. How you format the synopsis is up to you. Everyone seems to have their own opinions, but I think everyone can agree that something is better than nothing.
I don't know what the requirements are exactly, but if you have multiple ships on the board, is it supposed to be terminating after the first ship is hit, or is it supposed to let you keep going? EDIT: in this example, it keeps going.
Also note that there's nothing stopping you from winning by hitting the same ship repeatedly.
For an example of how varargin could be used:
function [playerwon] = Battleship3(varargin)
% PLAYERWON = Battleship3({OPTIONS})
% A simple battleship game. This would be a description of some sort.
%
% OPTIONS includes the key-value pairs:
% 'boardsize' specifies the size of the playing area (default [10 10])
% This is a 2-element vector specifying [rows columns].
% 'numguesses' specifies the number of attempts the user has
% before the session terminates. (default 10)
% 'numships' specifies the number of battleships on the board
% (default 5)
%
% Output argument PLAYERWON is a logical scalar
%
% Examples:
% Battleship3(); % start a new game with default settings
% Battleship3('boardsize',[20 20],'numships',10); % start with a larger board and more ships
%
% Battleship v.02
% programmed by Alex Skantz 10/13/21
% https://www.mathworks.com/matlabcentral/answers/1564041-turning-an-entire-script-into-a-function
% specify defaults
boardsize = [2 2];
numofguess = 10;
numships = 2;
% parse inputs
if numel(varargin)>0
k = 1;
while k <= numel(varargin)
switch lower(varargin{k})
case 'boardsize'
boardsize = varargin{k+1};
k = k+2;
case 'numguesses'
numofguess = varargin{k+1};
k = k+2;
case 'numships'
numships = varargin{k+1};
k = k+2;
otherwise
error('Battleship3: invalid option name %s',varargin{k})
end
end
end
% prepare console
clc
% ... everything else is the same
  2 Comments
DGM
DGM on 17 Oct 2021
I already gave you working examples to obviate most of these problems.
The "not enough arguments" error is almost certainly happening because you're calling the function without any arguments, like
Battleship3() % this will cause an error unless the code
instead of
Battleship3([10 10],5,2)
or with other parameter values.
The game parameters need to be set one of three ways:
  • The function takes no arguments; parameters are set interactively
  • The function has default parameter values which can be explicitly overridden by passing arguments
  • The function has no defaults; parameters must be passed as arguments
Your original script (and a lot of homework assignments) used the first approach, whereas my example (and most functions) use the second. What the above code looks like it's doing is both. You have to pick one and stick with it.
rowdim=boardsize('give me the number of rows on the board: ');
coldim=boardsize('give me the number of columns on the board: ');
This doesn't work because boardsize is an argument supplied to the function. It's a numeric vector, but here you're calling it like the input() function. In the convention I used,
rowdim = boardsize(1);
coldim = boardsize(2);
or you could just replace instances of one with the other (which is what I did).
Similarly, there are other issues inherited from the older code you posted, such as
battleship_row=randi(rowdim,[1 N]); %random row
battleship_col=randi(coldim,[1 N]); %random column
Here, N is not defined (it's commented out, but uncommenting won't fix this). Consider the first line. This creates a vector of size [1 N] containing random integers from 1 to rowdim, so this is the y-coordinate of N points in an array of height rowdim. N in this context is the number of ships, not the board size. This is why descriptive variable names are important.
GameBoard(battleship_row,battleship_col)=0;%%'0' hides the loaction of the battleship (unless it is correct)
This is harmless, but redundant. GameBoard is initialized to 0, so there's no point setting anything to zero.
[winloss]=results(rowguess,colguess,battleship_row,battleship_col);
If the nested function has been removed, this vestigal line is just going to throw an error.
hit=any((rowguess==battleship_row) & (colguess==battleship_col));
if hitaship
These two things need to be using the same variable name
numhits=1
Unless there's only ever one ship, this needs to increment; otherwise, the loop will never break (game is unwinnable).
frprintf('You sunk all the battleships! You won!')
That's just a typo, no biggie.
if~exist(numships,var)
numships=5
end
I don't know why this is at the end of the main loop. If default provision is done this way, it needs to be placed before the first case where the variable (numships) is used. Also, the arguments to exist() actually need to be strings/chars.
I'm sure I'm missing something else, but that's a first pass.
Deciding what you want to do and figuring out how to do it are two different parts of the problem that need to be solved in sequence. As I mentioned, it helps to write the synopsis first to nail down the conceptual behavior of what you want to write before you start writing it. I made choices for the sake of the examples I gave. You have to decide which choices you want to make.
A simple workflow for converting a script to a function might be
  • write the synopsis so you know what parameters need to be made accessible
  • adjust the code to make those parameters accessible
  • adjust the code to provide defaults or sanitize/validate the inputs
  • then lastly add the function header and get rid of placeholder parameter literals
That allows you to work on everything as a naked script, up until the last bit.

Sign in to comment.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by